didReceiveRemoteNotification not working in the background

40,884

Solution 1

Implementing didReceiveRemoteNotification and didReceiveRemoteNotification:fetchCompletionHandler is the correct way, but you also need to do the following:

Make sure to register for remote notifications, see documentation here:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{    
    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];

    return YES;
}

Also make sure to edit Info.plist and check the "Enable Background Modes" and "Remote notifications" check boxes:

enter image description here

Additionally, you need to add "content-available":1 to your push notification payload, otherwise the app won't be woken if it's in the background (see documentation here updated):

For a push notification to trigger a download operation, the notification’s payload must include the content-available key with its value set to 1. When that key is present, the system wakes the app in the background (or launches it into the background) and calls the app delegate’s application:didReceiveRemoteNotification:fetchCompletionHandler: method. Your implementation of that method should download the relevant content and integrate it into your app

So payload should at least look like this:

{
    aps = {
        "content-available" : 1,
        sound : ""
    };
}

Solution 2

  1. Register for push notification in app delegate.
  2. Add background mode in app capabilities.
  3. Add "content-available"="1" while sending the push notification(if you are using firebase replace "content-available"="1" by "content_available"="true" while sending the push notification from server side).

Solution 3

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::

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {

    self.application(application, didReceiveRemoteNotification: userInfo) { (UIBackgroundFetchResult) in

    }
}

Solution 4

I am creating an iOS project that will use Firebase Cloud Messaging (FCM) to deliver custom data elements, sent via Firebase/APNS notifications, from a company server to the iOS device.

The first thing I had to understand is that unlike Android, there is no similar type of 'Service' that will be able to capture and save information I'm sending, regardless if the app is in the foreground (active), background (still in memory) or not active (not in memory). Therefore, I have to use Notification messages NOT Data messages like I had designed for Android.

After much reading to understand both Apple APNS and the Firebase interface between the iOS app and APNS server, looking at countless posts on stackoverflow and other web resources, I finally figured out how to get this to work for my requirements.

When a Firebase Cloud Messaging (FCM) Notification message is sent from the server (or Firebase Console as the FCM defaults to Notification NOT Data messages), it is delivered via APNS and presented as a notification on the iOS device. When the user taps on the notification banner, iOS does the following: if the app is not running/loaded iOS launches the app, if the app is loaded/running but in the background iOS brings the app to the foreground OR if the app is in the foreground (all three cases), the message content is then delivered via func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {}.

One thing for sure, you Must Enable Background Modes and check Remote Notification, you DO NOT have to include {"content-available" : 1} in the payload.

1) Go through the APNS and Firebase setup, pretty straight forward, generate and register certificates and such.

2) In appDelegate, didFinishLaunchingWithOptions, add:

        Messaging.messaging().delegate = self as? MessagingDelegate

        if #available(iOS 10.0, *) {

            UNUserNotificationCenter.current().delegate = self

            let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]

            UNUserNotificationCenter.current().requestAuthorization(
                options: authOptions,
                completionHandler: {_, _ in })
        }
        else {

            let settings: UIUserNotificationSettings =
                UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)

            application.registerUserNotificationSettings(settings)
        }

        application.registerForRemoteNotifications()

3) Then add these call back functions to appDelegate:

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

        // Print message ID.
        if let messageID = userInfo["gcm.message_id"] {

            print("\n*** application - didReceiveRemoteNotification - fetchCompletionHandler - Message ID: \(messageID)")
        }

        // Print full message.
        print("\n*** application - didReceiveRemoteNotification - full message - fetchCompletionHandler, userInfo: \(userInfo)")

        myNotificationService?.processMessage(title: userInfo["Title"] as! String
            , text: userInfo["Text"] as! String, completion: { (success) in

                if success {
                    completionHandler(.newData)
                }
                else {
                    completionHandler(.noData)
                }
        })
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

        completionHandler([.alert, .badge, .sound])
    }

Very Helpful:

https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html

https://firebase.googleblog.com/2017/01/debugging-firebase-cloud-messaging-on.html

Solution 5

My device was in a bad state. I had to restart the device to get it working although I had done all the pre-requisites mentioned here.

Share:
40,884

Related videos on Youtube

YogevSitton
Author by

YogevSitton

Updated on July 09, 2022

Comments

  • YogevSitton
    YogevSitton almost 2 years

    I'm working on a big app with a huge chunk of legacy code. Currently - there's an implementation for:

    - (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
    

    The problem is that it is only called when the app is in the foreground OR when the user taps the the notification while the app is in the background. I tried to implement:

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

    But the app behaves the same. In any case - this method is not called when the app is in the background. What could be the problem?

    • KudoCC
      KudoCC almost 9 years
      It's normal that the method won't be called when app is in the background. System will notify you the notification through prompting a alert view or a banner.
    • Islam
      Islam almost 9 years
      What's the iOS version of your device? Is it at least 7? Because fetchCompletionHandler: requires minimum iOS 7.
  • YogevSitton
    YogevSitton almost 9 years
    I'm already calling registerForRemoteNotificationTypes and Remote Notifications is enabled
  • Baris Akar
    Baris Akar almost 9 years
    @godmoney See my update answer, you need to add "content-available":"1" to your notification payload...
  • Ashish Kakkad
    Ashish Kakkad almost 9 years
    @BarisAkar Great. I did not know about content-available payload. +1
  • priyanka gautam
    priyanka gautam over 8 years
    I am enabled Background mode for remote notifications and Payload should contain content-available:1 key-value pair But not call my background method
  • Baris Akar
    Baris Akar over 8 years
    @priyankagautam Sorry, but I don't know, what could cause your problem. Everything you need to do is actually described in this answer...
  • pqteru
    pqteru over 7 years
    what if I didn't want to call didReceiveRemoteNotification when I launch the app by icon?
  • Usman Nisar
    Usman Nisar over 7 years
    Hi ZaEem, have you found any solution
  • Wilson
    Wilson over 7 years
    when the app is the background, sending a remote push notification only this method is called func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
  • ZaEeM ZaFaR
    ZaEeM ZaFaR over 7 years
    @UsmanNisar I have added the workaround for my problem and its working fine for me. is it not working for you ?
  • Kirk Hammett
    Kirk Hammett over 7 years
    For those who are looking for this in new Xcode interface, take a look at your target -> Capabilities tab -> Background modes -> On
  • Yuchen
    Yuchen over 6 years
    @BarisAkar the link in the answer is broken. Could you help fix it?
  • Baran Emre
    Baran Emre about 6 years
    That did it. 1+
  • Gopik
    Gopik almost 6 years
    Without "sound": "" -> Thanks @onmyway133 It saved me..... iOS 10 its working fine... But not working on iOS 11, Any suggestions?
  • Michał Ziobro
    Michał Ziobro over 4 years
    Not Info.plist but Capabilities there you can add Background Modes
  • bigbubble
    bigbubble over 3 years
    The same issue is occurring for iOS 14. didReceiveRemoteNotification method never gets called when app is in background(not force close). Any suggestion for this? I followed the exact procedure and added "content-available" : 1 in the aps. didReceiveRemoteNotification only gets called in foreground.
  • ScottyB
    ScottyB over 3 years
    I thought I had this problem because the notification wasn't arriving immediately. Then I decided to wait, and wait, and wait. Over a full minute after receiving the "banner" notification while my app was in the background, my app delegate's didReceiveRemoteNotification method was called. No idea why it's taking so long, and I had to make a minor change to improve the UX.
  • ScottyB
    ScottyB over 3 years
    Came back to add that the long delay in receiving the notification in the background was only with APNS Sandbox environment. The notification to Prod is very quick.