How to observe adViewWillLeaveApplication Admob - ios

I am putting ads into my app, but when I click on the banner, it seems like there is a delay when calling my observer method to stop all timers, and are only stopped when the app leaves, not when the ad is clicked, which messes things up a little. so I implemented this method into my App delegate:
func adViewWillLeaveApplication(_ bannerView: GADBannerView) {
print("adViewWillLeaveApplication")
}
Now in the scene where I want to detect when an ad is clicked has this code:
NotificationCenter.default.addObserver(self, selector: #selector(pauseTimers), name: /* what goes here? */, object: nil)
I am unsure of what to put within the comments. I cannot find adViewWillLeaveApplication (and I have imported GoogleMobileAds).
What do I need to put instead?

Related

Need help identifying app state during phone call in iOS 14?

I have an app that does a certain task.
While performing this task, the app also listens for when the user receives a phone call.
If the user receives a phone call, this task needs to be interrupted.
On iOS 13 we listen to willResignActiveNotification for when the incoming call and to didBecomeActiveNotification for when the call ends (this for when the user has the app open before the phone call).
On iOS 14 this also works if the call setting is set to Full Screen.
But when this setting is changed to Banner these notifications are never triggered.
I can't identify the app state for when the setting is set to Banner. My guess is that it is still in the active state.
The problem is that although the notifications are not called, the UI is interrupted as if the app was placed in the background when the user has the Banner setting on.
Note
I also conform to CXCallObserverDelegate and implement callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) method to know when the user receives a call and when the call ends.
So a solution is to just resume the task when the call ends and this method is triggered.
But I want to understand the app lifecycle in this case which is not making sense to me.
Code Sample
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(appDidEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
timer.resume()
updateUI()
}
override func viewWillDisappear(_ animated: Bool) {
timer.invalidate()
pauseUI()
NotificationCenter.default.removeObserver(self, name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
super.viewWillDisappear(animated)
}
#objc private func appDidEnterBackground(_ notification: Notification) {
timer.invalidate()
pauseUI()
}
#objc private func appWillEnterForeground(_ notification: Notification) {
timer.resume()
updateUI()
}
Since iOS 10 our app uses callObserver:callChanged: from CXCallObserverDelegate to detect incoming or answered phone calls like:
#import CallKit;
#property (nonatomic) CXCallObserver *callObserver;
#pragma mark - Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Register Call Observer to detect incoming calls:
_callObserver = [[CXCallObserver alloc] init];
[_callObserver setDelegate:self
queue:nil];
}
#pragma mark - CXCallObserverDelegate
- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call {
// Based on state:
if (call.hasEnded) {
NSLog(#"CXCallObserver: Call has ended");
}
else if (call.hasConnected) {
NSLog(#"CXCallObserver: Call has connected");
}
else if (!call.isOutgoing && !call.hasConnected && !call.hasEnded) {
NSLog(#"CXCallObserver: Call is incoming");
}
else {
NSLog(#"CXCallObserver: None of the conditions");
}
}
Since the default behaviour on iOS 14 is to show a banner for incoming calls, we indeed saw that CXCallObserverDelegate was not called anymore.
When we changed the Phone setting in the iOS app to Full Screen it did work again. Obviously, just like in iOS 13 and before.
However, when we flipped the setting back to Banner, our app did call CXCallObserverDelegate event on an incoming banner call.
Based on the numerous issues with Local Network permission in iOS 14, it's only an estimated guess that here a similar root cause applies, where settings are only handed over to the native functionality once actively set by the user, and not on a default system setting.
I hope that explicitly changing the setting will solve your issue, or that we were accidentally lucky.
Note: the observations are done on iPhone SE 2nd gen with iOS 14.6.

When using AVFoundation, how to listen for Control Center Screen and Notification Center Screen activity

I use AVFoundation for a video recording. When the app goes to the background I stop the capture session and when it enters the foreground I restart the capture session, everything works fine. I also use callKit to listen for incoming phone calls and that works fine too:
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterBackground), name: UIApplication.willResignActiveNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
#objc func appWillEnterBackground() {
// if recording stop recording, stop timer, etc ...
captureSession.stopRunning()
previewLayer = nil
}
#objc func appWillEnterForeground() {
if !captureSession.isRunning {
captureSession.startRunning()
initialize preview layer
}
}
The problem is while the vc with the camera is active (recording or not) and when I swipe from the bottom to bring up the Control Center Screen or swipe from the top to bring down the Notification Center Screen UIApplication.willResignActiveNotification gets called and the capture session is stopped. When I remove either of those screens UIApplication.willEnterForegroundNotification doesn't get called and the capture session is no longer running.
What I want to do is when either of those screens surface I simply use a bool to prevent the capture session from stopping
var haveControlScreensSurfaced = false // toggle this true/false depending when the control screens enter and leave
#objc func appWillEnterBackground() {
if view.window != nil && haveControlScreensSurfaced { return }
// if recording stop recording, stop timer, etc ...
captureSession.stopRunning()
previewLayer = nil
}
How can specifically listen for Control Center Screen and Notification Center Screen activity so that I can toggle my haveControlScreensSurfaced bool value to true/false?
This way works good for an avplayer
Since I needed a capture session long story short I used this. When sliding up/down either the Notification Center Screen or the Control Screen, UIApplication.didEnterBackgroundNotification (app enters background) and UIApplication.willEnterForegroundNotification (app is about to enter foreground) never get called. I simply moved my code to there and problem solved:
NotificationCenter.default.addObserver(self, selector: #selector(didEnterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(appWillEnterForeground), name: UIApplication.willEnterForegroundNotification, object: nil)
#objc func didEnterBackground() {
// stop capture session
}
#objc func appWillEnterForeground() {
// start capture session
}
Here is a breakdown of what happens when the notifications are triggered:
Pressing the Home Button, sending app to the background:
1- `UIApplication.willResignActiveNotification` gets called first
2- `UIApplication.didEnterBackgroundNotification` gets called second // *** gets called when the HomeButton is pressed ***
Opening the app back up:
1- `UIApplication.willEnterForegroundNotification` gets called first // *** gets called when the opening the app back up ***
2- `UIApplication.didBecomeActiveNotification` gets called second
Sliding down the Notification Center Screen from the top:
1- `UIApplication.willResignActiveNotification` gets called first
2- `UIApplication.didBecomeActiveNotification` gets called second
3- if using the `.AVCaptureSessionWasInterrupted` the `.videoDeviceNotAvailableInBackground` gets called third
4- `UIApplication.willResignActiveNotification` gets called fourth
Sliding the Notification Center Screen back up:
1- `UIApplication.didBecomeActiveNotification` gets called alone
2- if using the `.AVCaptureSessionInterruptionEnded` it gets called second
Sliding the Control Screen up from the bottom:
1- `UIApplication.willResignActiveNotification` gets called alone
Sliding the Control Screen back back down:
1- `UIApplication.didBecomeActiveNotification` gets called by alone

I'm Trying to Pause a Sprite Kit Game on Exiting The App

I can pause the game I have created no problem while I'm in the app. However, when I try and call the pause function I have created in the applicationWillResignActive function in App Delegate, it doesn't work, at all. So this is my code that I am trying to use in my App Delegate File.
func applicationWillResignActive(_ application: UIApplication) {
GamePlayScene.instance.isPaused = true
print("Game is Paused")
}
The print Statement gets called so I know the function is getting called. However, the game still stays active. I'm trying to call the same isPaused function that I am using in my GamePlayScene.
One thing that works is if I just use the normal pause feature in SpriteKit but, I can't find a way to resume the game after I open the app. For example, I can use this.
func applicationWillResignActive(_ application: UIApplication) {
pause()
print("Game is Paused")
}
This seems to work, but the whole app just opens up frozen and there is no way to keep going. What do y'all think?
Figured it out. Since I don't see anyone else that has the answer when involving a SpriteKit Game, I will post the answer. This is my code.
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(self, selector: #selector(appMovedCameBack), name: Notification.Name.UIApplicationDidBecomeActive, object: nil)
#objc func appMovedCameBack() {
print("App Came Back!")
self.scene?.isPaused = true
GamePlayController.instance.pauseBtn?.alpha = 0
createPausePanel()
}
I am calling the function where I created the PausePanel and adding it to the scene. That is the createPausePanel() function. I am also setting the scene.isPaused = true. This is how I solved my issue.

How to free scene until Interstitial is dismissed?

So I have a an app that uses interstitial ads. Specifically it is a a SpriteKit game written with Swift.
I have code setup that when the user presses the replay button from the game over scene an ad appears and then it changes back to the game scene to replay the game. Now where I am running into problems the scene changes while the interstitial ad is being displayed, sometimes this doesn't happen fast enough and user can tap the restart button again, causing the game to crash.
Is there a way to freeze the screen and ignore any taps while the ad is being called? And also to only have the scene change after the ad is dismissed?
The code when the restart button is pressed;
if restartButton.contains(pointOfTouch) {
score = 0
ballMovementSpeed = 2
displayAd()
delay(2.0) {
self.restartScene()
}
I am a bit confused as to where the interstitialDelegate is placed. I am trying to implement func interstitialDidDismissScreen(ad: GADInterstitial!) {} to trigger a change back to my game scene and nothing happens when I dismiss the ad.
I have tried placing it in override func didMove(to view: SKScene){} as well as when the restart button is pressed and still won't work. This is how i have the ad being called
fun loadAndShow() {
myAd = GADInterstitial()
let requestI = GADRequest()
myAd.setAdUnitID("adID")
requestI.testDevices = [kGADSimulatorID, "test device"]
myAd.delegate = self
myAd.load(requestI)
}
func interstitialDidReceiveAd(_ ad: GADInterstitial) {
if (self.myAd.isReady) {
myAd.present(fromRootViewController: self)
}
}
you can use delegate methods of admob so when interstitial is going to be shown you can remove the restart button or put a condition so that it would not work when ad is shown. Also to pause the game is also important if it is running using isPaused bool.
https://developers.google.com/admob/ios/ad-events

SkScene update method interrupted by push notifications, how can i detect the interruption

I have a simple game app, which is programmed with SpriteKit. The Problem is, when a push notifications (SMS,iMessage etc) appears, the game stutters because the update:forScene: method is not called.
To avoid this i want to implement a simple pause menu, which will be shown as soon as a push message comes in.
How can i detect if a push message interrupts the app? In AppDelegate application:willResignActive is also not called.
It would be the best if the game continues when the message comes in, if there is another solution to force the update method to be called.
Had anybody the same Problem?
You should not try to resume your game when an interruption is happening, you should pause it, otherwise its not a good user experience.
For iMessages, phone calls etc you usually use the method you said doesn't work.
I use NSNotificationCenter to pause my games, you can google about it, there is plenty tutorials.
Essentially in your game scene add a NSNotificationCenter observer.
Also create a property for that observers name to avoid typos later on.
let pauseGameKey = "PauseGameKey" // above class so you can access it anywhere in project
class GameScene: SKScene {
// add this in didMoveToView
// in #selector add the method you want to get called
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(yourPauseGameMethod), name: pauseGameKey, object: nil)
}
Than create the willMoveFromView method so you can remove the observer when you transition to another scene (good practice).
override func willMoveFromView(view: SKView) {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
Than in app delegate post the notification when the application will resign.
func applicationWillResignActive(application: UIApplication) {
NSNotificationCenter.defaultCenter().postNotificationName(pauseGameKey, object: nil)
}
For local and remote UINotifications you can additional use these 2 methods in app delegate.
/// Local notification
func application(application: UIApplication, didReceiveLocalNotification notification: UILocalNotification) {
}
/// Remote notification
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
}
Hope this helps

Resources