82
votes

In iOS 9.3, the didReceiveRemoteNotification method gets called on both of the following occasions.

1) When the push notification is received 2) When the user launches the app by tapping on the notification.

But on iOS 10, I notice that the didReceiveRemoteNotification method does NOT fire when the user launches the app by tapping on the notification. It's called only when the notification is received. Hence, I cannot do any further action after the app is launched from notification.

What should be the fix for this? Any idea?

9
Working with ObjC or Swiftgurmandeep

9 Answers

135
votes

type converson

enter image description here

for Swift3

enter image description here

-

for sample see this

import the UserNotifications framework and add the UNUserNotificationCenterDelegate in Appdelegate

import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,UNUserNotificationCenterDelegate  


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    //create the notificationCenter
    let center  = UNUserNotificationCenter.current()
    center.delegate = self
    // set the type as sound or badge
    center.requestAuthorization(options: [.sound,.alert,.badge,  .providesAppNotificationSettings]) { (granted, error) in
        // Enable or disable features based on authorization

        }
        application.registerForRemoteNotifications()

    return true
}


 func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
 // let chars = UnsafePointer<CChar>((deviceToken as NSData).bytes)
  var token = ""

  for i in 0..<deviceToken.count {
//token += String(format: "%02.2hhx", arguments: [chars[i]])
   token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
  }

  print("Registration succeeded!")
  print("Token: ", token)
 }

 func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError) {
  print("Registration failed!")
 }

receive the Notifications using this delegates

 func userNotificationCenter(_ center: UNUserNotificationCenter,  willPresent notification: UNNotification, withCompletionHandler   completionHandler: @escaping (_ options:   UNNotificationPresentationOptions) -> Void) {
    print("Handle push from foreground")
    // custom code to handle push while app is in the foreground
    print("\(notification.request.content.userInfo)")
 }

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    print("Handle push from background or closed")
    // if you set a member variable in didReceiveRemoteNotification, you  will know if this is from closed or background
    print("\(response.notification.request.content.userInfo)")
}

func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {
let navController = self.window?.rootViewController as! UINavigationController
let notificationSettingsVC = NotificationSettingsViewController()
navController.pushViewController(notificationSettingsVC, animated: true)
}

for more Information you can see in Apple API Reference


objective C

AppDelegate.h has these lines:

Step-1

//Add Framework in your project "UserNotifications"
#import <UserNotifications/UserNotifications.h>  
@interface AppDelegate : UIResponder <UIApplicationDelegate,UNUserNotificationCenterDelegate>  

Step-2

AppDelegate.m

  // define macro
  #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)  

Step-3

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
application.applicationIconBadgeNumber = 0;
    if( SYSTEM_VERSION_LESS_THAN( @"10.0" ) ) {  
        [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeSound |    UIUserNotificationTypeAlert | UIUserNotificationTypeBadge |  UIUserNotificationTypeprovidesAppNotificationSettings) categories:nil]];  
        [[UIApplication sharedApplication] registerForRemoteNotifications];  

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

    return YES;
}

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

or use

  - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo  
{  
[self application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:^(UIBackgroundFetchResult result) {  
}];  
}  

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  
    NSLog(@"%@", notification.request.content.userInfo);
   }  

- (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  
     NSLog(@"%@", response.notification.request.content.userInfo);
    }  

    - (void)userNotificationCenter:(UNUserNotificationCenter *)center 
   openSettingsForNotification:(UNNotification *)notification{
        Open notification settings screen in app
   }
17
votes

Swift 4 and IOS 12.

While there might be multiple reasons (Already mentioned on other answers) why this issue could happen, in my personal case the solution was related to the payload when sending the push notification:

You need to set the "content-available" key on the json payload to 1.

e.g:

{"aps":{"alert":"Test", "content-available":1, "badge":1,"sound":"default"}}
16
votes

I had the same problem. Notification banner appeared, but -application:didReceiveRemoteNotification:fetchCompletionHandler: method was not called. The solution for me that worked was to add implementation of - application:didReceiveRemoteNotification: method and forward call to -application:didReceiveRemoteNotification:fetchCompletionHandler::

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    [self application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:^(UIBackgroundFetchResult result){}];
}

Source.

10
votes

Swift code:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.

    if #available(iOS 10.0, *) {
        let center = UNUserNotificationCenter.currentNotificationCenter()
        center.delegate = self
    }

    // ...

    return true
}

@available(iOS 10.0, *)
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {        
    print(response.notification.request.content.userInfo)        
}

@available(iOS 10.0, *)
public func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    print(notification.request.content.userInfo)
}
7
votes

Working version iOS 11, Swift 4, Xcode 9. Just copy paste the below code in AppDelegate.

import UIKit
import UserNotifications
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,UNUserNotificationCenterDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        if #available(iOS 10, *)
        { // iOS 10 support
            //create the notificationCenter
            let center = UNUserNotificationCenter.current()
            center.delegate = self
            // set the type as sound or badge
            center.requestAuthorization(options: [.sound,.alert,.badge]) { (granted, error) in
                if granted {
                    print("Notification Enable Successfully")
                }else{
                    print("Some Error Occure")
                }
            }
            application.registerForRemoteNotifications()
        }
        else if #available(iOS 9, *)
        {
            // iOS 9 support
            UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil))
            UIApplication.shared.registerForRemoteNotifications()
        }
        else if #available(iOS 8, *)
        {
            // iOS 8 support
            UIApplication.shared.registerUserNotificationSettings(UIUserNotificationSettings(types: [.badge, .sound,
                                                                                                     .alert], categories: nil))
            UIApplication.shared.registerForRemoteNotifications()
        }
        else
        { // iOS 7 support
            application.registerForRemoteNotifications(matching: [.badge, .sound, .alert])
        }
        return true
    }


    //get device token here
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken
        deviceToken: Data)
    {
        let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
        let token = tokenParts.joined()
        print("Registration succeeded!")
        print("Token: ", token)

        //send tokens to backend server
    }

    //get error here
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error:
        Error) {
        print("Registration failed!")
    }

    //get Notification Here below ios 10
    func application(_ application: UIApplication, didReceiveRemoteNotification data: [AnyHashable : Any]) {
        // Print notification payload data
        print("Push notification received: \(data)")
    }

    //This is the two delegate method to get the notification in iOS 10..
    //First for foreground
    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (_ options:UNNotificationPresentationOptions) -> Void)
    {
        print("Handle push from foreground")
        // custom code to handle push while app is in the foreground
        print("\(notification.request.content.userInfo)")
    }
    //Second for background and close
    @available(iOS 10.0, *)
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response:UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)
    {
        print("Handle push from background or closed")
        // if you set a member variable in didReceiveRemoteNotification, you will know if this is from closed or background
        print("\(response.notification.request.content.userInfo)")
    }


}
6
votes

It is an iOS bug. It will be fixed in iOS 10.1. But just wait for 10.1 release in Oct instead of implementing a new library and remove it later.

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

5
votes

BTW, this issue seems to be fixed in iOS 10.1. I tested my app on 10.1, all work fine

1
votes

swift 4, if you are using ios 11 or xcode version greater than 9.0 then you must have use UNUserNotification delegate method to call the didReceiveRemoteNotification

  func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

//Your code to handle events

}
0
votes

I think this is done like this by design, I'm currently working on iOS 14 and the same happens. What I realized is that if you enable 'Background Fetching' capability and implement the [didReceiveRemoteNotification: withCompletionHandler:] «you may also want to do so with [performBackgroundFetch:] as well», then you can set a breakpoint in that delegate method and debug remote notification