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:
- application:didReceiveRemoteNotification does not properly detect the app's state (second thread above).
- 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)
- 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
- 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]; |
| 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 |
| { |
| |
| } |
Then, when a user taps a notification, this fires:
| |
| -(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void |
| (^)(UIBackgroundFetchResult))completionHandler |
| { |
| |
| |
| if( SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO( @"10.0" ) ) |
| { |
| NSLog( @"iOS version >= 10. Let NotificationCenter handle this one." ); |
| |
| return; |
| } |
| NSLog( @"HANDLE PUSH, didReceiveRemoteNotification: %@", userInfo ); |
| |
| |
| |
| 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" ); |
| |
| } |
| |
| - (void)userNotificationCenter:(UNUserNotificationCenter *)center |
| didReceiveNotificationResponse:(UNNotificationResponse *)response |
| withCompletionHandler:(void (^)())completionHandler |
| { |
| NSLog( @"Handle push from background or closed" ); |
| |
| } |
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.