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.
Related
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.
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.
It's very important for my app to be notified every time the user kills my app (not moving it to the background, but actually quitting it altogether by double-tapping the Home button and swiping my app screenshot up). I know applicationWillTerminate() barely ever gets called, so I was wondering what the safest way is to be notified when a user will kill the app. It seems very unlikely that Apple hasn't provided ANY form of solution for this, as a lot of iOS users make it a custom to kill apps when they're done using it, so it would be really really REALLY weird if it would be impossible to catch this event. The only thing I need to do is fire off a URLRequest to send some updating instructions to my web service.
I get more consistency when I add an observer to handle UIApplicationWillTerminateNotification. It gets called when the user double-taps the Home button and swipe the app to kill it.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
NSNotificationCenter.defaultCenter().addObserver(self, selector:#selector(AppDelegate.onAppWillTerminate(_:)), name:UIApplicationWillTerminateNotification, object:nil)
return true
}
func onAppWillTerminate(notification:NSNotification)
{
print("onAppWillTerminate")
}
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.
I have properly registered for the push notification.
Implemented following method to receive notification.
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
Also used UIApplicationLaunchOptionsRemoteNotificationKey from func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool.
I'm able to receive notification when the app is active. I'm also able to see the notification if I open the app from the notification by selecting it from banner or alert.
But if app is not active, it could be alive or killed and if a notification arrives at that point. And I ignore the notification and open the app by directly selecting the app icon from the home screen, I'm not getting the notification information in the UIApplicationLaunchOptionsRemoteNotificationKey from func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool.
So what is the right way to handle this scenario? Should the app call server and get the recent messages it sent? Any help is much appreciated.
If the user will open you app directly from it's home screen, you would not get access to the push notification dictionary.
If you need the data sent in the push notification, so the proper way will be a server request as you suggested.
If user enter the app by click the icon or launch from the background, the push notification message can't deliver to you, the message is cached by system, the only way to get it is from notification center
Answering my question:
It is possible for the app in the background to receive push notification.
To do so send content-available in the aps dictionary of push notification.
Enable background mode of the app from the Background modes section of the Capabilities tab in your Xcode project.
Implement application:didReceiveRemoteNotification:fetchCompletionHandler: in the app delegate.
References:
See section 'Notification Payload' in https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html
See section 'Using Push Notifications to Initiate a Download' in
https://developer.apple.com/library/prerelease/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html
However if app is killed then the appdelegate's application:didReceiveRemoteNotification:fetchCompletionHandler: is not getting called.
According to Will iOS launch my app into the background if it was force-quit by the user? post on iOS8.0 it is possible to do it using PushKit. But I have not tested it. And also if VOIP is not used then I dont know if it is possible for app to receive push notification information if it is killed.