For iOS 10, will application:didReceiveRemoteNotification:fetchCompletionHandler: still be called when app is in background? Or we need to implement something new.

Hi,

I am trying to make our app's push notification work on iOS 10 and have found application:didReceiveRemoteNotification:fetchCompletionHandler: won't be called when app is in background with xcode 8. The new function from UNUserNotificationCenterDelegate:

func userNotificationCenter(UNUserNotificationCenter, willPresent: UNNotification, withCompletionHandler: (UNNotificationPresentationOptions) -> Void) looks like only for a foreground app. So how could we handle the app background case?

Besides this, does anyone know how to make the new two functions for receiving notifications and handling the selections of custom actions to be called in xcode? My breakpoint didn't work with them.


Thanks!

Replies

Yes we are wondering the same thing.


It may be required now to implement a notification service extension if you want to handle background pushes, but would like someone to confirm

Check out these threads:

https://forums.developer.apple.com/thread/60295

https://forums.developer.apple.com/thread/54415


I've been fighting iOS 10 push notifications for a few days, but I think I've got the trick.


Here are the bullet points:


  1. application:didReceiveRemoteNotification does not properly detect the app's state (second thread above).
  2. If you use the new UNUserNotificationCenter stuff, you must call this: [[UIApplication sharedApplication] registerForRemoteNotifications]; or you will never get a token for a registered device, and nothing will respond to the notification (first thread above)
  3. If you add registerForRemoteNotifications (bullet point 2), application:didReceiveRemoteNotification will fire when a user responds to a push notification, which brings us back to the beginning of the circle that began with not properly detecting the app's state
  4. Using UNUserNotificationCenterDelegate (required to know the app state when a notification is tapped in iOS 10) AND application:registerForRemoteNotifications (required to get the app to accept notifications at all) causes methods for both delegates to fire when a user responds to a push notification


So, right or wrong, this is what I've got, and it seems to work:

AppDelegate.h has these lines:

#import <UserNotifications/UserNotifications.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate,UNUserNotificationCenterDelegate>


These macros are defined:

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)


AppDelegate.m has this:

if( SYSTEM_VERSION_LESS_THAN( @"10.0" ) )
{
  [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge) categories:nil]];
  [[UIApplication sharedApplication] registerForRemoteNotifications];

  if( options != nil )
  {
    NSLog( @"registerForPushWithOptions:" );
  }
}
else
{
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;
  [center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error)
  {
    if( !error )
    {
      [[UIApplication sharedApplication] registerForRemoteNotifications]; // required to get the app to do anything at all about push notifications
      NSLog( @"Push registration success." );
    }
    else
    {
      NSLog( @"Push registration FAILED" );
      NSLog( @"ERROR: %@ - %@", error.localizedFailureReason, error.localizedDescription );
      NSLog( @"SUGGESTIONS: %@ - %@", error.localizedRecoveryOptions, error.localizedRecoverySuggestion );
    }
   }];
}


This will fire as a result of calling registerForRemoteNotifications:

- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
// custom stuff we do to register the device with our AWS middleman
}


Then, when a user taps a notification, this fires:

// This will fire in iOS 10 when the app is foreground or background, but not closed
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void
  (^)(UIBackgroundFetchResult))completionHandler
  {
   // iOS 10 will handle notifications through other methods

    if( SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO( @"10.0" ) )
    {
      NSLog( @"iOS version >= 10. Let NotificationCenter handle this one." );
     // set a member variable to tell the new delegate that this is background
      return;
    }
    NSLog( @"HANDLE PUSH, didReceiveRemoteNotification: %@", userInfo );

    // custom code to handle notification content

    if( [UIApplication sharedApplication].applicationState == UIApplicationStateInactive )
    {
      NSLog( @"INACTIVE" );
      completionHandler( UIBackgroundFetchResultNewData );
    }
    else if( [UIApplication sharedApplication].applicationState == UIApplicationStateBackground )
    {
      NSLog( @"BACKGROUND" );
      completionHandler( UIBackgroundFetchResultNewData );
    }
    else
    {
      NSLog( @"FOREGROUND" );
      completionHandler( UIBackgroundFetchResultNewData );
    }
}


Then for iOS 10, these two methods:

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
    willPresentNotification:(UNNotification *)notification
  withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  NSLog( @"Handle push from foreground" );
  // custom code to handle push while app is in the foreground
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
  withCompletionHandler:(void (^)())completionHandler
{
  NSLog( @"Handle push from background or closed" );
 // if you set a member variable in didReceiveRemoteNotification, you will know if this is from closed or background
}


It seems a bit of a mess, but it's working, and I can't find enough documentation or code examples to show me that there is a better way. I've tried lots of things that don't work.


The fact that two different clases have to fire delegate methods for the same event makes me believe that the UNUserNotification stuff isn't yet complete. Maybe I'm wrong.

I believe it is still broken on watchOS 3 (XCode 8b3).


Tried everything but can't get a notification to appear on the watch when it moves into the background but delegate functions work fine on watch which does not make sense since the watch is intended to not be in the foreground most of the time.


Even tried adding Healthkit and setting up background processing to see if that would help but it does not.


If anyone has any code for watch OS 3 that works please post.


Thanks


Greg

As of beta 7, our shipping app is not having application:didReceiveRemoteNotification:fetchCompletionHandler: called at all. The app launches or foregrounds, but nothing is called that indicates the trigger was a notification. Has anyone seen any updates on this?

Does anyone have an update or an official statement from Apple? As of Beta 8 this works on some devices and it doesn't on some. We're experiencing the same issue: after `registerForRemoteNotifications`, neither `application:didFailToRegisterForRemoteNotificationsWithError` nor `application:didRegisterForRemoteNotificationsWithDeviceToken` are being called. But this only happens the second time, as stated previously. We also found out that if you avoid `unregisterForRemoteNotifications` everything works as expected, you get the the token after calling `registerForRemoteNotifications` again.

see this thread for why some local notifications work and others do not, this may be similar to the remote notification issue:


https://forums.developer.apple.com/thread/50460




This bug is not yet fixed.



Greg

Exaclty the same case for me. The "application:didReceiveRemoteNotification:fetchCompletionHandler" is not called at all when the app is opended after a user taped on a notification. This completely breaks the behaviour of user notification hanling.


The delegate method that actually gets called is the "application:didReceiveRemoteNotification:" which is marked as deprecated on iOS 10


I've just tried again with XCode 8 GM btw :/

I have faced a similar problem.
On IOS 9.xx and below, application:didReceiveRemoteNotification:userInfo fetchCompletionHandler: is called twice, once when the user receive a remote push notification and a second time if the user tap on the notification to open the app.

Since IOS 10, application:didReceiveRemoteNotification:userInfo fetchCompletionHandler: is call we a push notification is receive while application: didReceiveRemoteNotification: is called when the user tap on it.

This behavior is contrary to the documentation that state for the latter that:

"If the user opens your app from the system-displayed alert, the system may call this method again when your app is about to enter the foreground so that you can update your user interface and display information pertaining to the notification."

I am unsure why the behavior change but it is clearly different from ios 8 or 9.

I ended up implementing both method and using the APP state to figure what action has been taken.

@renzo.crisostomo did you find a solution for the problem that you can't re-register your app for push notifications? I ran into the same problem and can't find any solution.

Have you tried this in 10.1 beta? Is this still an issue?


Thanks

Fixed in iOS 10.1 !!!!

This issue is still happening on iOS 10.1 beta 2. Apps built for iOS 9 with Xcode 7 do not call the 'didReceiveRemoteNotification' methods a second time on iOS 10.

I can also confirm that re-registering seems somehow to be broken (running on iOS 10.0.2). Calling UNUserNotificationCenter.currentNotificationCenter().requestAuthorizationWithOptions ends always with granted = false in the completion handler (no matter if I tap on the allow or deny button on the permission dialog).

Fixed in 10.1 for me as well !!! Was dealing with same issue in iOS 10.

On 10.1 - push notif handling went back to how it has been on 9.x.x - application:didReceiveRemoteNotification:userInfo fetchCompletionHandler - is getting called twice again - Upon receive and tap.