I'm trying to call a method in my iOS app only when I enter in it from a dead state, meaning that the app isn't running in the background. I have searched for it and I came up with the method:
applicationWillEnterForeground:
But when I put some prints inside this method in my app delegate, the prints are not called at any time. Specifically what I want to do is to call a service to check wether the user's token access is valid or not, so I would show him the Log In view or the home view.
The app delegate method you're asking for is:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool
This is called when the app finishes launching, which occurs when it was previously not in a background or suspended state.
That said, for your use case, I'd put this in:
func applicationWillEnterForeground(_ application: UIApplication)
This gets called anytime the app enters the foreground which is more appropriate for this problem. If it's not being called (as you suggest), I expect that you haven't declared the method correctly (declare it exactly as above), or you haven't declared it on the UIApplicationDelegate.
For full details, including how to handle the new scene delegates in iOS 13+ (which are different, and tied to the UISceneDelegate), see Managing Your App's Life Cycle.
Related
Since I'm not using Storyboard-based layout on my iOS project, I am wondering about the following issue:
AppDelegate's method:
application(_:didFinishLaunchingWithOptions:)
Is calling not only on user-initiated app launch, but on occasions, the push notifications, VOIP push notifiactions, location-based interactions can call this method from the background as well, and since I am initiating a window and an initial view controller there, like so
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window!.rootViewController = MainRouter().viewController()
window!.makeKeyAndVisible()
}
I am wondering is my application in risk of termination by the OS if, for example, push notifications, or location-based self-launching call application(_:didFinishLaunchingWithOptions:) when app is in terminated state? Since it is documented for Voip push notifications that is is highly recommended to avoid complex CPU usage, heavy UI loading, API calling for cases when app self-launches from terminated state?
The delegate method
application(_:didFinishLaunchingWithOptions:)
is always called once.
The mentioned services can cause to launch the app – if it's currently not running – and the affected service is indicated in the LaunchOptionsKey.
If the app is running but not in the foreground Push Notifications, Location Services etc. call other methods.
As we all know, application(_:didFinishLaunchingWithOptions:) callback is called after app is almost ready to run, but there are cases, that push notifications, voip-push notifications, etc. do call this method as well, when app terminated to perform some tasks.
I need a solid answer, is there a way to tell when app launch was user-initiated, or something else did order app launch from background?
Is it safe to assume that the method parameter launchOptions: [UIApplication.LaunchOptionsKey : Any]? will always be nil on user-initiated launch?
Is it safe to assume that the method parameter launchOptions: [UIApplication.LaunchOptionsKey : Any]? will always be nil on user-initiated launch?
No, absolutely not. For example if the user launches your app by way of a Quick Action, that is user-initiated but the Quick Action will be in the launch options.
However, if the launchOptions is empty, you can pretty much rely on the fact that the user tapped the app's icon in the springboard.
in our app we are logging (with an external analytics service) every time the user open our app.
However, we have noticed that there are many weird app sessions (almost 15% of total sessions).
Following the device timestamp of the events, the following methods are called:
application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)
applicationDidBecomeActive(_ application: UIApplication)
viewDidAppear(_ animated: Bool) // App Splash Screen
applicationDidEnterBackground(_ application: UIApplication)
Looking at the timestamp, from didFinishLaunchingWithOptions to applicationDidEnterBackground there is just a very short delay (<1 sec).
Our hypothesis is that the system sometimes wakes up the app without it being actually opened and "seen" by the user. But we can't find any docs about this kind of use case, and we can't reproduce ourself the issue.
Did someone have experienced something similar?
EDIT:
I want to add that these suspicious sessions are often (but not always) linked to an app update.
I'm not sure...
I suppose if app wasn't user terminated nor suspended but was just put out of memory because other apps needed the memory, then almost a launch options can wake the app up if needed. Some examples:
geofence
interaction with push notification's actions. Example if you have userNotificationCenter(_:didReceive:withCompletionHandler:) implemented...
silent notification
background app refresh
Take a look at here and see what launch options are possible for your app. I'm not sure if all of them can actually launch the app or what...
If you want more visibility into this, then make sure for future you log the launchoptionskey that caused the app to get launched...
You need more info - I would extend your logging to dump the launchOptions dictionary in
application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)
Looking at UIApplication.LaunchOptionsKey, there are a lot of options which may be involved. Note, as the help says: The contents of this dictionary may be empty in situations where the user launched the app directly
if let lop = launchOptions {
let toLog = lop.map{"\($0.0.rawValue) \($0.1)"}.joined(separator:"\n")
// log the options
...}
I did have an idea this might be related to previewing in AppSwitcher but that doesn't seem to trigger didFinishLaunchingWithOptions, unless you actually bring the app to the front. Maybe some split-screen stuff on iPad? It feels like the app is being invoked in a context where it can't get past the launch screen.
I want to clear the app data when the user removes the app from the app switcher by swiping up after the app is kept in the background about 3 to 4 hours.
Is there any function or delegate to know when the app is killed without coming into the foreground.
I want the login page to be open when the user kills the app from the app switcher. Otherwise, it should open the home page.
Solution for Swift:
In your AppDelegate.swift File add the following function (if not present):
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// clean your apps data here
cleanAppData()
//show the login
showLogin()
}
This function is only called if the app is "cold started".
This means:
it is called if the app was started for the first time
if the app was "killed" by iOS
if the app was "killed" by the user
it is not called if the app runs in the background and is opened again
Then add the following function (if not present) to AppDelegate.swift:
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
// show the homepage
showHomePage()
}
This function is only called if the app is "warm started" which means if the app was running before and sent to the background by the user.
You can save the current time in UIApplicationDelegate.applicationWillTerminate(_:) and UIApplicationDelegate.applicationDidEnterBackground(_:) to the user defaults. When the app comes back to the foreground or is launching you read this time from user defaults.
Here is my scenario:
The app receives some notifications, in order to be able to have actions on my locked screen they are in silent mode (content-available = 1 on aps payload).
In didReceiveRemoteNotification callback I save it to core data (background or foreground).
My problem is:
Assuming the app was already running and I have locked the screen, when I tap on some notification no callback is called. So far I'm not able to find the proper callback that handles this event.
Am I missing something?
I need to have the notification list saved and be able to go to the proper view when some notification arrives (and is tapped).
By the way receiving the notification, saving, going to the proper view ... everything works except when the screen is locked.
I suspect if your app was running when notification came because if application is in inactive state and notification is tapped then didReceiveRemoteNotification is called by iOS.
However, if for some reason, application is not running then didReceiveRemoteNotification function is not called. In such cases, if user click notification, app is launched by notification and didFinishLaunchingWithOptions gets called:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var userinfo : NSDictionary = launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
print(launchOptions);
return true
}
Please check once in your didFinishLaunchingWithOptions function.