54

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.

mfaani
  • 33,269
  • 19
  • 164
  • 293
mverderese
  • 5,314
  • 6
  • 27
  • 36
  • It's seems fine. When you opened the app from icon this method never be called. So exactly what is the problem you are facing or what else you want to achieve ? – Arpit Kulsreshtha Feb 28 '14 at 03:13
  • @ArpitKumarKulshrestha that's not true. Since the app supports background remote notifications, this method is called while the app is still in the background. For example, if I put a break point here and close the app. Then when I receive the push, it hits the breakpoint with the app still closed. That did not happen in iOS6 – mverderese Feb 28 '14 at 03:16
  • In your Question line "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.". Specify in which iOS it is running ? – Arpit Kulsreshtha Feb 28 '14 at 03:25
  • You can't do this in iOS 6. http://stackoverflow.com/questions/20487890/how-to-get-apns-push-notification-in-background-in-ios6 – Arpit Kulsreshtha Feb 28 '14 at 03:25
  • I'm running this on iOS 7. We are not supporting anything before iOS 7 – mverderese Feb 28 '14 at 03:26
  • I think you should check mark the push notification background mode to enable it. OR Enter in Plist to enable background mode for push notification. I think you aware about that. – Arpit Kulsreshtha Feb 28 '14 at 03:29
  • @Arpit You're missing my question entirely. This method is being called every time I get a push, like it should. The issue is that in the background, a new view is being pushed onto the navigation stack. I'd only like that to happen if the user swipes from the notification (they expect to see the contents right away). If they hit the icon, they should be taken to the home screen, where we have a badge telling the user that there is new content for them in their feed. – mverderese Feb 28 '14 at 03:32
  • Same issue is getting in ios 10 ,in 9.3 it is working fine and below solutions are failing in ios 10 – Abhishek Thapliyal Sep 29 '16 at 20:37

3 Answers3

88

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

mfaani
  • 33,269
  • 19
  • 164
  • 293
mverderese
  • 5,314
  • 6
  • 27
  • 36
  • 3
    Thank you so much!!!! This was VERY helpful!!!! Anyone who is stuck with figuring out how to implement background remote notifications, see this page: http://developer.xamarin.com/guides/cross-platform/application_fundamentals/backgrounding/part_3_ios_backgrounding_techniques/updating_an_application_in_the_background/ – Sandy D. Feb 23 '15 at 19:10
  • 3
    Just a little optimization. If you are calling completionHandler(UIBackgroundFetchResultNewData); in each conditional, you can call it unconditionally and remove two of these lines. – Moebius May 11 '15 at 14:19
  • 8
    The problem with the `UIApplicationStateInactive` state is that it happens not only if the app is being opened by user's interaction with the notification but also when the app is in the `Inactive` state, e.g.: when a sms/call is received, Notification Center is shown or Control Center. Therefore, it results in false positives where it navigates to the view in the cases mentioned above. – Tom Kraina May 26 '15 at 16:31
  • 2
    Thanks a lot for writing that you get TWO calls for each notification! Saved my day :) However, remember to enable Background fetch and Remote notifications in your project -> Capabilities -> Background Modes – Lukasz Czerwinski Jul 31 '15 at 00:55
  • 4
    Note: the reason it's called twice is due to the PN having `content_available=true`. If you remove it or set it to false, it will only run once. This will not solve everyone's problem since some people need that key to be true – Tim Aug 26 '15 at 13:33
  • Tim, i have to have the content available set to false otherwise nothing is sent. any suggestions? i am also unable to get notifications while the app is completely switched off. could this be related? – Rob85 Dec 07 '15 at 13:19
  • @TomKraina as for when your app is opened after a dismissed message or finished call, your state would be: `UIApplicationStateInactive`...OK. Understood. But I don't get why at that moment you would get a callback from your `didReceiveRemoteNotification` method!? I mean you're not receiving a notification at that moment. I don't understand the flow you're talking about. – mfaani Jun 27 '17 at 18:09
  • could me tell me how to start with it @Mike V – Dilip Tiwari Sep 14 '17 at 13:42
  • content_available : 0 fixed my issue. I am checking it on 2018 and it saved my life :) thanks – regeint Jul 20 '18 at 11:13
12

@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)
    }
}
SuperGlenn
  • 572
  • 6
  • 9
9

@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)
    }

}
Marie Amida
  • 556
  • 1
  • 5
  • 14