Detect if the app was launched/opened from a push notification
See This code :
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{ if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground ) { //opened from a push notification when the app was on background }}
same as
-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
late but maybe useful
When app is not running
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
is called ..
where u need to check for push notification
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];if (notification) { NSLog(@"app recieved notification from remote%@",notification); [self application:application didReceiveRemoteNotification:notification];} else { NSLog(@"app did not recieve notification");}
The issue we had was in correctly updating the view after the app is launched. There are complicated sequences of lifecycle methods here that get confusing.
Lifecycle Methods
Our testing for iOS 10 revealed the following sequences of lifecycle methods for the various cases:
DELEGATE METHODS CALLED WHEN OPENING APP Opening app when system killed or user killed didFinishLaunchingWithOptions applicationDidBecomeActive Opening app when backgrounded applicationWillEnterForeground applicationDidBecomeActiveDELEGATE METHODS CALLED WHEN OPENING PUSH Opening push when system killed [receiving push causes didFinishLaunchingWithOptions (with options) and didReceiveRemoteNotification:background] applicationWillEnterForeground didReceiveRemoteNotification:inactive applicationDidBecomeActive Opening push when user killed didFinishLaunchingWithOptions (with options) didReceiveRemoteNotification:inactive [only completionHandler version] applicationDidBecomeActive Opening push when backgrounded [receiving push causes didReceiveRemoteNotification:background] applicationWillEnterForeground didReceiveRemoteNotification:inactive applicationDidBecomeActive
The problem
Ok, so now we need to:
- Determine if the user is opening the app from a push
- Update the view based on the push state
- Clear the state so that subsequent opens don't return the user to the same position.
The tricky bit is that updating the view has to happen when the application actually becomes active, which is the same lifecycle method in all cases.
Sketch of our solution
Here are the main components of our solution:
- Store a
notificationUserInfo
instance variable on the AppDelegate. - Set
notificationUserInfo = nil
in bothapplicationWillEnterForeground
anddidFinishLaunchingWithOptions
. - Set
notificationUserInfo = userInfo
indidReceiveRemoteNotification:inactive
- From
applicationDidBecomeActive
always call a custom methodopenViewFromNotification
and passself.notificationUserInfo
. Ifself.notificationUserInfo
is nil then return early, otherwise open the view based on the notification state found inself.notificationUserInfo
.
Explanation
When opening from a push didFinishLaunchingWithOptions
or applicationWillEnterForeground
is always called immediately before didReceiveRemoteNotification:inactive
, so we first reset notificationUserInfo in these methods so there's no stale state. Then, if didReceiveRemoteNotification:inactive
is called we know we're opening from a push so we set self.notificationUserInfo
which is then picked up by applicationDidBecomeActive
to forward the user to the right view.
There is one final case which is if the user has the app open within the app switcher (i.e. by double tapping the home button while the app is in the foreground) and then receives a push notification. In this case only didReceiveRemoteNotification:inactive
is called, and neither WillEnterForeground nor didFinishLaunching gets called so you need some special state to handle that case.
Hope this helps.