How to call function when app regains focus? - ios

My user is redirected to their inbox when they verify their email. When they reopen the application I need to reload the user's data automatically. I used the following code to do so...
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in
Auth.auth().currentUser?.reload()
}
However, this code only works if I completely close the application and reopen it. Is there a way for me to call this without having to close the application?

You can try didBecomeActiveNotification:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
Auth.auth().currentUser?.reload()
}

A slightly different approach but should do the same,
In ViewDidAppear:
NotificationCenter.default.addObserver(self, selector: #selector(yourFunction), name: UIApplication.didBecomeActiveNotification, object: nil)
#objc function yourFunction(){
print("Returned from background")
// Do something
}

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

How to check that user is back from Settings

I am sending local notifications to my users, and I want to show the relevant title on the notification settings button.
If local notifications are off, this title should be "Notifications: off", and if local notifications are on, this title should be something like "Preferences".
Right now I'm checking this in viewDidLoad and viewDidAppear, and it works.
if UIApplication.sharedApplication().currentUserNotificationSettings()?.types.rawValue == 0 {
//the first title
} else {
//the second title
}
Except one case. If my user is changing notifications in the phone settings from "on" to "off" or vice versa, and after that he is back — the title is not changing (because this viewController already loaded and did appear).
How could I check that user is back from the Settings?
You can observe this notification when your app is come to foreground from inactive, the selector will be called everytime your app is opened again from background:
Put in viewDidLoad:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.reloadData), name: UIApplicationWillEnterForegroundNotification, object: UIApplication.sharedApplication())
Put in viewDidDissapear or deinit:
NSNotificationCenter.defaultCenter().removeObserver(self)
Swift 5.5:
NotificationCenter.default.addObserver(
self,
selector: #selector(yourFunction),
name: UIApplication.willEnterForegroundNotification,
object: UIApplication.shared
)
And:
deinit {
NotificationCenter.default.removeObserver(self)
}

table view not getting refreshed on opening app

When the app is open or in background, I am using notification in appdelegate to update the message box table and then it is working.
Appdelegate (in didFinishLaunchingWithOptions and didReceiveRemoteNotification):
NSNotificationCenter.defaultCenter().postNotificationName("changeMessageBox", object: nil)
MessageViewController:
in viewdidload:
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector (MessagesViewController.reloadtable), name: "changeMessageBox", object: nil)
func reloadtable()
{
clearArrays()
if (PFUser.currentUser()!["firebaseUID"] !== nil)
{
self.updateResultArray(PFUser.currentUser()!["firebaseUID"] as! String)
resultsTable.reloadData()
}
}
and data is refreshed in the message box.
But when the app is closed and i recieve a chat, the message box ( the window with messages from all the people not the individual chat) does not get updated.
I have to open the app, go to message box ,then back to main page and go back to message box again only then the table view gets refreshed.
Do you guys know what am i missing?
Try below code for post notification,
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("changeMessageBox", object: nil)
})
I was having same issue and resolved like this. Hope this will help you.
Receiving Notification while app in closed or in suspended state will trigger
application:handleActionWithIdentifier:forRemoteNotification:completionHandler:
Try to post notification in this method. i guess it will update your view.
Additionally Refer this document if you have query with Notifiacation
http://samwize.com/2015/08/07/how-to-handle-remote-notification-with-background-mode-enabled/

Stop method's execution - Swift

When my watchKit app goes to background it fires the delegate method applicationWillResignActive. Method documentation says it can be used to pause ongoing tasks.
I have an ongoing method that i want to be stopped or broken by the use of the external method. How do i do that?
Example
func method1(){
// performing some actions
}
func breakMethod1(){
// running this method can stop (break) the execution of method1
}
This is, of course, assuming that your app has been architected so that breakMethod1() will definitely cancel the action occurring in method1().
You should set up an observer for an NSNotification at the beginning of method1() like so:
let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.addObserver(self, selector: "breakMethod1", name: UIApplicationWillResignActiveNotification, object: nil)
And for the sake of cleanup, you should also remove this observer after it's been triggered like so:
notificationCenter.removeObserver(self, name: UIApplicationWillResignActiveNotification, object: nil)

Resources