didReceiveRemoteNotification: fetchCompletionHandler: open from icon vs push notification

IosIos7Push NotificationApple Push-NotificationsUiapplicationdelegate

Ios Problem Overview


I'm trying to implement background push notification handling, but I'm having issues with determining whether the user opened the app from the push notification that was sent as opposed to opening it from the icon.

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

    //************************************************************
    // I only want this called if the user opened from swiping the push notification. 
    // Otherwise I just want to update the local model
    //************************************************************
    if(applicationState != UIApplicationStateActive) {
	    MPOOpenViewController *openVc = [[MPOOpenViewController alloc] init];
	    [self.navigationController pushViewController:openVc animated:NO];
	} else {
		///Update local model
	}

	completionHandler(UIBackgroundFetchResultNewData);
}

With this code, the app is opening to the MPOOpenViewController regardless of how the user opens the app. How can I make it so that the view controller is only pushed if they open the app from swiping the notification?

With the same code, this worked on iOS 6, but with the new iOS 7 method, it doesn't behave how I want it to.

Edit: I'm trying to run the app on iOS 7 now, and we are not supporting any version prior to iOS 7. I used this same exact code in the iOS 6 version of the method (without the completion handler) and it behaved the way I'd expect it to. You'd swipe the notification and this would get called. If you opened from the icon, the method would never be called.

Ios Solutions


Solution 1 - Ios

Ok I figured it out. The method is actually called twice (once when it receives the push, and once when the user interacts with the icon or the notification).

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {

	if(application.applicationState == UIApplicationStateInactive) {
        
        NSLog(@"Inactive");

        //Show the view with the content of the push

        completionHandler(UIBackgroundFetchResultNewData);
        
    } else if (application.applicationState == UIApplicationStateBackground) {
        
        NSLog(@"Background");
        
        //Refresh the local model

        completionHandler(UIBackgroundFetchResultNewData);

    } else {
        
        NSLog(@"Active");
        
        //Show an in-app banner

        completionHandler(UIBackgroundFetchResultNewData);

    }
}

Thanks Tim Castelijns for the following addition:

> Note: the reason it's called twice is due to the Payload having > content_available : 1. If you remove the key and its value, then it will only run upon tapping. This will not solve everyone's problem since some people need that key to be true

Solution 2 - Ios

@MikeV's solution in Swift 3 (but with switch statement):

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

    switch application.applicationState {

    case .inactive:
        print("Inactive")
        //Show the view with the content of the push
        completionHandler(.newData)
        
    case .background:
        print("Background")
        //Refresh the local model
        completionHandler(.newData)
        
    case .active:
        print("Active")
        //Show an in-app banner
        completionHandler(.newData)
    }
}

Solution 3 - Ios

@MikeV's solution in Swift 2:

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
    
    if(application.applicationState == UIApplicationState.Inactive)
    {
        print("Inactive")
        //Show the view with the content of the push
        completionHandler(.NewData)

    }else if (application.applicationState == UIApplicationState.Background){
        
        print("Background")
        //Refresh the local model
        completionHandler(.NewData)

    }else{
        
        print("Active")
        //Show an in-app banner
        completionHandler(.NewData)
    }

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMike VView Question on Stackoverflow
Solution 1 - IosMike VView Answer on Stackoverflow
Solution 2 - IosSuperGlennView Answer on Stackoverflow
Solution 3 - IosMarie AmidaView Answer on Stackoverflow