38

I have a problem with FCM notification on iOS.

I receive notifications with success when my app is in foreground (the callback didReceiveRemoteNotification in appdelegate is fired), but I don't receive notifications when the app is in background (I do not see anything in the notification tray of iOS).

So, I think the problem is in the format of the message sent by FCM. The json sent by my server to FCM, is in the following format:

{  
   "data":{  
      "title":"mytitle",
      "body":"mybody",
      "url":"myurl"
   },
   "notification":{  
      "title":"mytitle",
      "body":"mybody"
   },
   "to":"/topics/topic"
}

As you can see, there are two blocks in my json: one notification block (to receive notifications in background), and one data block (to receive notifications in foreground).

I cannot understand why notifications in background are not received. My doubts are about the order of the blocks (is a problem if I put the "data" block before the "notification" block?).

EDIT: More info about the problem.

This is my appdelegate.swift:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
    var window: UIWindow?


    // Application started
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool
    {
        let pushNotificationSettings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
        application.registerUserNotificationSettings(pushNotificationSettings)
        application.registerForRemoteNotifications()

        FIRApp.configure()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "tokenRefreshNotification:", name: kFIRInstanceIDTokenRefreshNotification, object: nil)

        return true
    }




    // Handle refresh notification token
    func tokenRefreshNotification(notification: NSNotification) {
        let refreshedToken = FIRInstanceID.instanceID().token()
        print("InstanceID token: \(refreshedToken)")

        // Connect to FCM since connection may have failed when attempted before having a token.
        if (refreshedToken != nil)
        {
            connectToFcm()

            FIRMessaging.messaging().subscribeToTopic("/topics/topic")
        }

    }


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


    // Handle notification when the application is in foreground
    func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
            // If you are receiving a notification message while your app is in the background,
            // this callback will not be fired till the user taps on the notification launching the application.
            // TODO: Handle data of notification

            // Print message ID.
            print("Message ID: \(userInfo["gcm.message_id"])")

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


    // Application will enter in background
    func applicationWillResignActive(application: UIApplication)
    {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }



    // Application entered in background
    func applicationDidEnterBackground(application: UIApplication)
    {
        FIRMessaging.messaging().disconnect()
        print("Disconnected from FCM.")
    }



    // Application will enter in foreground
    func applicationWillEnterForeground(application: UIApplication)
    {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }



    // Application entered in foreground
    func applicationDidBecomeActive(application: UIApplication)
    {
        connectToFcm()

        application.applicationIconBadgeNumber = 0;
    }



    // Application will terminate
    func applicationWillTerminate(application: UIApplication)
    {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }


}

The only way I can receive messages in foreground, is by disabling method swizzling, setting FirebaseAppDelegateProxyEnabled to NO in my info.plist.

In this case, FCM documentation says that I have to implement in my appdelegate.swift two methods:

 - FIRMessaging.messaging().appDidReceiveMessage(userInfo)  in didReceiveRemoteNotification callback
 - FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Sandbox) in didRegisterForRemoteNotificationsWithDeviceToken callback

But if I implement those functions, messages stops to arrive even when the app is in foreground.

I know this is very strange.

EDIT 2:

When the app is in background the notification isn't received, but when i open my app, the same notification is received immediately (method didReceiveRemoteNotification is fired).

Cœur
  • 37,241
  • 25
  • 195
  • 267
Mark O' Brian
  • 393
  • 1
  • 3
  • 8
  • Just out of interest, did you try to set the priority to high? – Chris Jun 18 '16 at 18:33
  • yes, but nothing changed. still no notifications in background. – Mark O' Brian Jun 18 '16 at 18:35
  • 1
    Try adding [content_available](https://developers.google.com/cloud-messaging/http-server-ref#downstream-http-messages-json) with true value. If it doesn't work, try removing `priority` while using `content_available`. I'm not so familiar on how APNS works, but there's a chance that the message will be ignored if both `priority` and `content_available` is in a single payload. Let me know if it works. Cheers! – AL. Jun 19 '16 at 23:50
  • Have you tried it out @MarkO'Brian? Did it work? – AL. Jun 20 '16 at 08:53
  • i am the same problem ! fcm don't send the message to apns – ios_dotnet_superuser Jun 24 '16 at 11:28
  • 1
    I had the same problem, but I already have the priority set to `High` and this did not help – Ryan Cocuzzo Oct 28 '16 at 00:25
  • setting the priority to `high` didn't help me either. I do get foreground messages, and background messages work on Android, just not iOS. Not sure what's going on – Andrew Stromme Dec 15 '16 at 03:00
  • @astromme did you get this working? – user2363025 Jan 06 '17 at 14:27
  • @user2363025 Yes! I needed to add the push notification entitlement. I did this by my target settings in Xcode, then Capabilities, then turned on "Push Notifications" – Andrew Stromme Jan 07 '17 at 05:42
  • @RyanCocuzzo try my answer http://stackoverflow.com/a/41518242/1004331 which fixed the issue for me. Does that work for you? – Andrew Stromme Jan 07 '17 at 05:47
  • @astromme I had that enabled already but thanks – user2363025 Jan 09 '17 at 09:26

6 Answers6

23

Assuming you've set up everything correctly, then setting the priority of the message from normal to high should make it appear immediately. This is due to the way iOS bundles notifications and handles them. You can read about the Priority of FCM notifications here. Please note that you shouldn't really use high in production unless there is a good case for it, as it has a battery penalty.

Here is the reference from Apple's docs

The priority of the notification. Specify one of the following values:

10–Send the push message immediately. Notifications with this priority must trigger an alert, sound, or badge on the target device. It is an error to use this priority for a push notification that contains only the content-available key.

5—Send the push message at a time that takes into account power considerations for the device. Notifications with this priority might be grouped and delivered in bursts. They are throttled, and in some cases are not delivered. If you omit this header, the APNs server sets the priority to 10.

Community
  • 1
  • 1
Chris
  • 7,830
  • 6
  • 38
  • 72
  • hi, thank you for your answer! Assuming that this is the problem, what should i have to do to receive background notifications in production mode without setting priority to high? – Mark O' Brian Jun 18 '16 at 17:38
  • You just wait for it to appear. iOS decides when to display it. See my updated answer – Chris Jun 18 '16 at 17:39
  • ok, it seems legit.. but what about messages i've sent 2 hours ago and not arrived yet? i mean, those are messages sent through a development APNS certificate, not production. Is there a difference in terms of forwarding time between development messages and production messages? – Mark O' Brian Jun 18 '16 at 17:47
  • 1
    Assuming that you have set it all up correctly, then you should definately be seing them sooner than that. But consider that when you send a notification to your app when it's in the background and you don't see it showing up, then opening up the app, then that notification fires immediately and is no longer in the queue. Makes sense? As I said, this is all assuming that you have followed Firebase's guide on how to it up for bg notifications correctly and it works as it should. You haven't provided much code to verify that, so I made some strong assumptions in my answer – Chris Jun 18 '16 at 17:52
  • hi, i've updated my question with more code and details - thank you! – Mark O' Brian Jun 18 '16 at 18:05
  • Thanks! We were having the same issue and it turns out that priority high fixed it. Without a priority none of them were coming in while in the background. – Jer Jun 28 '16 at 13:52
  • 8
    I can confirm that the problem was the priority. After I set priority to high, my problem has solved. Thank you guys! – Mark O' Brian Jul 06 '16 at 14:11
  • @MarkO'Brian can you send me the payload json. I am not getting proper format of json to send notification using FCM. – Sagar Snehi Jan 03 '17 at 07:24
  • @MarkO'Brian can you give us an example of working code for background ios push ? – Simone Campagna Oct 03 '18 at 10:56
19

You need to set the content_available property to true like so:

{  
   "data":{  
      "title":"mytitle",
      "body":"mybody",
      "url":"myurl"
   },
   "notification":{  
      "title":"mytitle",
      "body":"mybody",
      "content_available": true
   },
   "to":"/topics/topic"
}

There is a blue note box on in this section that states this: https://firebase.google.com/docs/cloud-messaging/concept-options#notifications

Keith Holliday
  • 1,692
  • 21
  • 15
  • 2
    This answer is important for whom, like me, forgot to put the "notification" object. – marceloquinta Jul 22 '16 at 03:41
  • 14
    From https://firebase.google.com/docs/cloud-messaging/http-server-ref, the content_available key should be in the same level as data, notification, to, and not inside notification. – Peacemoon Nov 22 '16 at 12:32
  • @Peacemoon when I put content_available : 1 at the same level as data, I get the error: FIRMessaging receiving notification in invalid state 2, When I use content-aavailable : 1, I no longer get a warning, either way, I am getting the notifications in the tray and they are not silent. Any ideas? – user2363025 Jan 06 '17 at 14:26
  • 2
    @user2363025 content_available should be true instead of 1 due to how Firebase Messaging processes the object. content_available is for GCM, content-available is for APNS. – c0deblooded Apr 03 '17 at 19:43
  • Thanks, this format helped me a lot in making a notification that works for both Android and iOS. – Lucy Nov 16 '17 at 01:32
  • Thank you sooo much. You saved my life. – Abel Tilahun Jan 14 '21 at 19:53
12

Priority and content_available (as mentioned in other answers) are the key elements to make sure you receive the notifications. Tests showed interesting results, so I thought to share them here.

Test Results: Swift 3, Xcode 8, iOS 10

Priority = "high" => "immediate" (within obvious network delays) reception of message.

Priority = "normal" => various results (generally fast, though obviously slower than "high")

content_available = true in the notifications (no payload message)

  • Foreground = data received as expected
  • Background = data received as expected (when opening the app)

content_available = true in the top level (no payload message)

  • Foreground = data received as expected
  • Background = data received as expected (when opening the app)

content_available = true in the notifications (with message {title/body})

  • Foreground = data received TWICE
  • Background = data received TWICE (when opening the app)

content_available = true in the top level (with payload message)

  • Foreground = data received TWICE
  • Background = data received TWICE (when opening the app)

CONCLUSIONS:

  1. While Priority is a possible cause of not receiving messages, the MOST IMPORTANT factor is that you must have either 'content_available' or a payload message.
  2. content_available MUST be used on data-only payloads (without it, no message is ever sent).
  3. content_available SHOULD NOT be used on payloads that contain messages as it causes double messages to be sent from FCM.
  4. No difference found in the use of content_available in the top level or in the notifications.

EDIT: Additional testing results: - if you have a msg title you MUST have a msg body or you don't get an alert.

The odd part of this is that you WILL get the vibrate, badge and sound, but the alert box won't show up unless you have a body as well as the title.

Apps-n-Add-Ons
  • 2,026
  • 1
  • 17
  • 28
9

You might need to add the push notification entitlement. Do this by going to your target settings, then clicking "Capabilities" and turning "Push Notifications" on.

Target Capabilities

Andrew Stromme
  • 2,120
  • 23
  • 30
2

-For FCM when application is in background or foreground and OS <10 application(_:didReceiveRemoteNotification:) method will fire.

-When application is foreground and OS => 10 userNotificationCenter:willPresentNotification:withCompletionHandler: method will fire.

-When sending data message without notification component: application(_:didReceiveRemoteNotification:) method will fire.

-When sending data message with notification component : userNotificationCenter:willPresentNotification:withCompletionHandler: method will fire.

ireshika piyumalie
  • 2,226
  • 22
  • 22
  • from Firebase documentation, is it not the `func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage)` that has to be called instead of `application(_:didReceiveRemoteNotification:)` for direct messages? – Lohith Korupolu Feb 03 '20 at 15:39
-2

I had this issue, with the content_available property set. The solution was to remove and reinstall the application from the iOS device.

azmeuk
  • 4,026
  • 3
  • 37
  • 64