Firebase Cloud Messaging iOS: No background push notification
Asked Answered
H

7

17

Using the newer Firebase cloud messaging with method swizzling i am successfully able to receive a payload in the didReceiveRemoteNotification method in my app delegate when my app is foregrounded. However i do not get any sort of payload and didReceiveRemoteNotification does not get called when my app is backgrounded, despite the api response that the message is successfully sent (see below)
Here is the request that i send to the FCM api to trigger a push notification https://fcm.googleapis.com/fcm/send

{
"to": "{valid-registration-token}",
"priority":"high", //others suggested setting priority to high would fix, but did not
  "notification":{
      "body":"Body3",
      "title":"test"  
 } 
}


with response from FCM

{
      "multicast_id": 6616533575211076304,
      "success": 1,
      "failure": 0,
      "canonical_ids": 0,
      "results": [
        {
          "message_id": "0:1468906545447775%a4aa0efda4aa0efd"
        }
      ]
    }

Here is my appDelegate code

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        Fabric.with([Crashlytics.self])
        FIRApp.configure()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.tokenRefreshNotification),
                                                         name: kFIRInstanceIDTokenRefreshNotification, object: nil)
        return true
    }



    func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
        // Let FCM know about the message for analytics etc.
        FIRMessaging.messaging().appDidReceiveMessage(userInfo)

        // Print full message.
        print("%@", userInfo)

        // handle your message

//        let localNotification = UILocalNotification()
//        localNotification.fireDate = NSDate()
//        let notification = userInfo["notification"]!
//        localNotification.alertBody = notification["body"] as? String
//        localNotification.alertTitle = notification["title"] as? String
//        localNotification.timeZone = NSTimeZone()
//        application.scheduleLocalNotification(localNotification)

    }

    func tokenRefreshNotification(notification: NSNotification) {
        let refreshedToken = FIRInstanceID.instanceID().token()!
        print("InstanceID token: \(refreshedToken)")
        LoginVC.registerForPushNotifications()
        connectToFcm()
    }


    //foreground messages
    func applicationDidBecomeActive(application: UIApplication) {
        connectToFcm()
    }

    // [START disconnect_from_fcm]
    func applicationDidEnterBackground(application: UIApplication) {
        FIRMessaging.messaging().disconnect()
        print("Disconnected from FCM.")
    }


    func connectToFcm() {
        FIRMessaging.messaging().connectWithCompletion { (error) in
            if (error != nil) {
                print("Unable to connect with FCM. \(error)")
            } else {
                print("Connected to FCM.")
            }
        }
    }

}

I call this code at a later flow in my app to ask for permissions

static func registerForPushNotifications() {
    let settings: UIUserNotificationSettings =
                UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
            UIApplication.sharedApplication().registerUserNotificationSettings(settings)
            UIApplication.sharedApplication().registerForRemoteNotifications()
}

Since i am able to receive notifications while app is foregrounded i assume this would allay all concerns that my apns certificates are not uploaded or that the registration token is incorrect. If that is not the case, please comment and i'll go down that rabbit hole again. There's probably something simple that i am overlooking, but how can i get the notifications to appear while the app is backgrounded? Thanks

Hambley answered 19/7, 2016 at 5:58 Comment(6)
I am facing a similar struggle today: the handler in app delegate is called, when the application is in the foreground, but no tray notification appears, when the app is in the background. Interesting to see if anything turns up. Will let you know if I figure this out.Silures
Have you taken a look at this issue on github?Silures
When your app is in the foreground you can receive messages directly from FCM not through APNs which is what is used when your app is in the background. To be sure that APNs is configured correctly could you confirm that you are receiving an APNs token by implementing the didRegisterForRemoteNotificationsWithDeviceToken method.Highwayman
@ArthurThompson Before this version of the code, i was not using the method swizzling technique and did implement the didRegisterForeRemoteNotificationsWIthDeviceToken method where i was able to get a token. At first i'd hoped i was done, but when this token didn't work for FCM i had to refactor. Does that answer your question?Hambley
@Silures Yes i read the whole thing. I tried setting priority to high and content-available to true. but no luck. I also tried using both at the same time. but others have said that that will not work, and in my case that was again true.Hambley
Seems like APNs sandbox was having some issues (forums.developer.apple.com/thread/52224) but have now been resolved, are you still seeing issues with this?Highwayman
H
12

Apparently, (despite using method swizzling that should be doing it automatically) you must still set the APNS token on the Firebase Instance. Consequently this means you must also implement the didRegisterForRemoteNOtificationsWithDeviceToken method with something like

func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {


        // set firebase apns token
        FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Unknown)
}

As an added note. Using "priority":"high" was useful for testing as it sent the notification right away.

Hambley answered 20/7, 2016 at 4:29 Comment(4)
As soon as i got everything working, i did a fresh install and now the FIRInstanceID.instanceID().token object is nil and i can't connectToFcm with error code 501. Any ideas?Hambley
The key for me was selecting "Unknown" as the type. Previously I tried both "Sandbox" and "Development", but it wasn't working correctly either way. The token object is nil when launching a clean installation, however I got around that with a guard check, and the token refresh notification is fired multiple times from what I can see.Silures
When the app has swizzling enabled it does set the APNs token automatically, so no need to implement didRegisterForRemoteNotificationsWithDeviceTokenHighwayman
@ArthurThompson so, why even using an updated version, I had to do this to solve this background notification problem? In fact, Google's documentation for Firebase has lots of guides with deprecated and unclear methods.Colophony
P
4

The desired behavior, at least for my app, is for the arrival of a push notification where the content of the PN (Push Notification) should be shown in a desired page in the app.

I've three different app states where a PN can arrive: (of course it depends on your implementation, but you should get an idea how apple processes the PN in these states)

(the behavior is determined by apple!)

1) the app is in foreground:

  • PN received
  • pops up in the Notification Center (you can either click on it or not - it does not matter)
  • badge will be updated
  • PN is available in the desired page

In this case the app always processes the PN

2) the app is in background:

  • PN received
  • pops up in the Notification Center (you can either click on it or not - it does not matter)
  • badge will be updated
  • PN is available in the desired page

In this case the app always processes the PN

3) the app is killed/closed:

  • PN received
  • pops up in the Notification Center
  • now there are three possible ways

    1. swipe notification away -> PN will be NOT processed by the app

    2. open the app via the launcher icon directly -> PN will be NOT processed by the app

    3. click on the notification -> PN will be processed by the app

For this case, if the user does not open the app by tapping the notification, there is no way to get the notification data.

To be independent of the app state, you need to setup a server, where you can request the specific data. E.g. you've a view which shows all notifications, you need an API to request specific data. You shouldn't process the data in the receiving method of the Pushnotification.

I hope that helps someone!

Pesek answered 19/2, 2019 at 6:19 Comment(0)
P
3

Make sure that you have added "content_available" : true in the JSON payload. Otherwise you won't get push notification in the background mode.

"notification" : {
            "body" : "this is body",
            "title" : "this is title"
}
"content_available" : true
Perfoliate answered 23/10, 2017 at 9:41 Comment(1)
You Rock :))) )))))Blearyeyed
A
1

Similarily to Daniel George's solution I could fix my problem with:

Messaging.messaging().apnsToken = deviceToken

FirebaseInstanceID 2.0.8, FirebaseMessaging 2.0.8

Amersfoort answered 14/2, 2018 at 14:42 Comment(2)
What is exactly deviceToken ? How can we find it ?Reject
It's available here: func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) You can find information to this here: firebase.google.com/docs/cloud-messaging/ios/client It's supposed to be the token firebase uses to identify your device by.Amersfoort
S
0

Follows these step after getting successful token from FCM to enable the background payload data fetch using FCM:

  1. open Project Navigator > AppCapabilities > Background Modes > check for remote notifications

  2. In App.delegate define:

    func application(_ application: UIApplication,didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    
        print("userinfo in background mode on-\(userInfo)")
    
        completionHandler(UIBackgroundFetchResult.newData)
    }
    
  3. Format your payload correctly according to apple guidelines: enter link description here

  4. Do not forget to add key value pair: {"content-available":1} unless you won't able to fetch payload in background.

now try to send the notification again using FCM token. Have a happy coding

Shankle answered 11/5, 2018 at 10:53 Comment(0)
P
0

I was struggling to understand why my iOS14 Notifications weren't going through. This is not mentioned on the Firebase Docs.

On iOS14, December 2020, I can confirm that @Daniel George's solution still stands and fixed my notifications from not coming through. Reposting with updated Syntax.

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Messaging.messaging().apnsToken = deviceToken }

iOS14.1, Swift 5.3, Firebase and Firebase Cloud Messaging as up to date on Dec 18th 2020.

Phlegm answered 19/12, 2020 at 5:11 Comment(0)
E
-4

AFAIK, for ios you wont be able to use FCM for send push notification messages for apps in background or killed state.

Firebase Push Notification

Exuberance answered 14/9, 2017 at 6:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.