Quickstart-ios: FCM Swift - Application does not receive message data in background - iOS 10

Created on 7 Apr 2017  ·  25Comments  ·  Source: firebase/quickstart-ios

Hello,

Since Parse.com is closed, I changed my communication module to Firebase. It would seem that my application does not receive a message (_notification or data_) in background.

I tried to send different messages:

{
    "to": "My_Super_FCM_Token",
    "content_available": true,
        "data" : {
            "Nick" : "Mario",
            "Room" : "PortugalVSDenmark"
        }
}
{
    "to": "My_Super_FCM_Token",
    "priority": "high",
        "data" : {
            "Nick" : "Mario",
            "Room" : "PortugalVSDenmark"
        }
}
{
    "to": "My_Super_FCM_Token",
    "priority": "high",
    "content_available": true,
        "data" : {
            "Nick" : "Mario",
            "Room" : "PortugalVSDenmark"
        }
}

The application can receive messages (_notification or data_) in foreground. It works perfectly in foreground.

Here is my code from my AppDelegate.swift:

import UIKit
import Foundation

import Firebase
import FirebaseInstanceID
import FirebaseMessaging

import UserNotifications

import XCGLogger

let log = XCGLogger.default

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    let gcmMessageIDKey = "gcm.message_id"

    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        //adapt the storyboard if the device is an iPad or a iPhone
        let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)//self.adaptStoryboard()
        self.window?.rootViewController = storyboard.instantiateInitialViewController()
        self.window?.makeKeyAndVisible()

        // MARK: Init Notification
        registerForPushNotifications(application: application)

        // Add observer for InstanceID token refresh callback.
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.tokenRefreshNotification),
                                               name: .firInstanceIDTokenRefresh,
                                               object: nil)

        if let token = FIRInstanceID.instanceID().token() {
            print("TOKEN....")
            print(token)
            connectToFcm()
        }

        return true
    }

    func applicationWillResignActive(_ application: UIApplication) {
    }
    func applicationDidEnterBackground(_ application: UIApplication) {
        log.info("Application: DidEnterBackground")
    }
    func applicationWillEnterForeground(_ application: UIApplication) {
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        log.info("Application: DidBecomeActive")
        connectToFcm()
    }
    func applicationWillTerminate(_ application: UIApplication) {
    }
}

extension AppDelegate {
    /**
     Register for push notification.

     Parameter application: Application instance.
     */
    func registerForPushNotifications(application: UIApplication) {
        print(#function)
        if #available(iOS 10.0, *) {
            // For iOS 10 display notification (sent via APNS)
            UNUserNotificationCenter.current().delegate = self

            let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
            UNUserNotificationCenter.current().requestAuthorization(
                options: authOptions,
                completionHandler: {_, _ in })

            // For iOS 10 data message (sent via FCM)
            FIRMessaging.messaging().remoteMessageDelegate = self
            log.info("Notification: registration for iOS >= 10 using UNUserNotificationCenter")
        } else {
            let settings: UIUserNotificationSettings =
                UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(settings)
            log.info("Notification: registration for iOS < 10 using Basic Notification Center")
        }
        application.registerForRemoteNotifications()
        FIRApp.configure()
    }

    func tokenRefreshNotification(_ notification: Notification) {
        print(#function)
        if let refreshedToken = FIRInstanceID.instanceID().token() {
            log.info("Notification: refresh token from FCM -> \(refreshedToken)")
        }

        // Connect to FCM since connection may have failed when attempted before having a token.
        connectToFcm()
    }

    func connectToFcm() {
        // Won't connect since there is no token
        guard FIRInstanceID.instanceID().token() != nil else {
            log.error("FCM: Token does not exist.")
            return
        }

        // Disconnect previous FCM connection if it exists.
        FIRMessaging.messaging().disconnect()

        FIRMessaging.messaging().connect { (error) in
            if error != nil {
                log.error("FCM: Unable to connect with FCM. \(error.debugDescription)")
            } else {
                log.info("Connected to FCM.")
            }
        }
    }

    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        log.error("Notification: Unable to register for remote notifications: \(error.localizedDescription)")
    }

    // This function is added here only for debugging purposes, and can be removed if swizzling is enabled.
    // If swizzling is disabled then this function must be implemented so that the APNs token can be paired to
    // the InstanceID token.
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

        var token = ""
        for i in 0..<deviceToken.count {
            token = token + String(format: "%02.2hhx", arguments: [deviceToken[i]])
        }
        log.info("Notification: APNs token: \((deviceToken as! NSData))")
        log.info("Notification: APNs token retrieved: \(token)")
        // With swizzling disabled you must set the APNs token here.
        /*FIRInstanceID
         .instanceID()
         .setAPNSToken(deviceToken,
         type: FIRInstanceIDAPNSTokenType.sandbox)*/
    }

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
        log.info("Notification: basic delegate")
        // 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.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }

        analyse(notification: userInfo)
    }

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        log.info("Notification: basic delegate (background fetch)")
        // 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.
        // Print message ID.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }

        analyse(notification: userInfo)
        completionHandler(UIBackgroundFetchResult.newData)
    }
}

@available(iOS 10, *)
extension AppDelegate: UNUserNotificationCenterDelegate {
    // Receive displayed notifications for iOS 10 devices.
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler:
        @escaping (UNNotificationPresentationOptions) -> Void) {
        log.info("Notification: iOS 10 delegate(willPresent notification)")
        let userInfo = notification.request.content.userInfo
        // Print message ID.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }

        analyse(notification: userInfo)
        completionHandler([])
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        log.info("Notification: iOS 10 delegate(didReceive response)")

        let userInfo = response.notification.request.content.userInfo
        // Print message ID.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        }

        // Print full message.
        analyse(notification: userInfo)
        completionHandler()
    }
}

extension AppDelegate: FIRMessagingDelegate {
    // Receive data message on iOS 10 devices while app is in the foreground.
    func applicationReceivedRemoteMessage(_ remoteMessage: FIRMessagingRemoteMessage) {
        log.info("Notification: Firebase FCM delegate remote message.")
        analyse(notification: remoteMessage.appData)
    }
}

Settings

Environment

  • iOS 10
  • Xcode 8.3.1
  • Apple Swift version 3.1 (swiftlang-802.0.51 clang-802.0.41) Target: x86_64-apple-macosx10.9

Pods

  • Using Firebase (3.15.0)
  • Using FirebaseAnalytics (3.7.0)
  • Using FirebaseCore (3.5.2)
  • Using FirebaseInstanceID (1.0.9)
  • Using FirebaseMessaging (1.2.2)

Info.plist

  • FirebaseAppDelegateProxyEnabled: NO

Regards,
Ysee

Most helpful comment

I fixed my issue by deleting the FirebaseAppDelegateProxyEnabled key from Info.plist and updating the APNs certificates.

Now it works perfectly using "content_available": trueto receive messages while application is in background.

Regards,

All 25 comments

I fixed my issue by deleting the FirebaseAppDelegateProxyEnabled key from Info.plist and updating the APNs certificates.

Now it works perfectly using "content_available": trueto receive messages while application is in background.

Regards,

Hi YMonnier,
Your question is helped me so much. I have tried to implement the same appDelegate file,
but I have some troubles,

  • I can get FCM messages, when the application is running, but there is no notification popup.
  • in background i cannot get messages until I open the app again. after opening i can get the message, but again there is no notification popup.
  • analyse function is a log function or in that function you have something to display popup,
    thanks in advance

Hi @ahmeric,

Below my answers about your troubles.

  • _I can get FCM messages, when the application is running, but there is no notification popup._

Did you try to send a payload from you server like this?

https://fcm.googleapis.com/fcm/send
Content-Type:application/son
Authorization:key=YOUR_SERVER_FCM_KEY

{
    "content_available": true,
    "notification" : {
    "body" : "This is a Firebase Cloud Messaging Topic Message!",
    "title" : "FCM Message",
  },
  "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}

With this payload, the application can display notification popup.
However, if you send a payload with only the data key:

{
    "content_available": true,
    "data": {
        "people": {
            "123": {"name":"Jane Doe", "age":35},
            "124": {"name":"John Doe", "age":37}
        }
    },
    "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}

your application will not display the notification popup.
Of course you are able to use both of these keys into payload (data & notification)

  • _in background i cannot get messages until I open the app again. after opening i can get the message, but again there is no notification popup._

If you set the content_available: true into your payload, app will able to received messages in background.

  • _analyse function is a log function or in that function you have something to display popup,
    thanks in advance_

analyse is just my parsing function to insert some data etc...

Regards,

Thanks for your detailed explanations.
I am using curl to send notification from WordPress server,
curl --header "Authorization: key=FCM_SERVER_KEY” --header Content-Type:"application/json" https://fcm.googleapis.com/fcm/send -d "{\"registration_ids\":[ \”IOS_TOKEN\”, \”ANDROID_TOKEN\”], \"data\": { \"title\": \"Title Text\", \"content\”:\”Post Content\”, \"post_id\”:\”123\”}}”
I am sending both to android and IOS. In android side, I can build a notification with these informations, I need the same in the IOS. I have searched lots of solutions but yours is worked fine, just to need an notification alert.
is there any way to build it without "notification" object?

Thanks,

Yes you can easily build a notification in iOS.
Here the code in Swift:

func notification(message msg : String, timeInterval : TimeInterval){
        let localNotification = UILocalNotification()
        localNotification.alertBody = msg
        localNotification.fireDate = Date(timeIntervalSinceNow: timeInterval)
        localNotification.soundName = UILocalNotificationDefaultSoundName
        UIApplication.shared.scheduleLocalNotification(localNotification)
    }

Does it also run in the background while the app is not running?

While the app is not running, I do not know. You have to try ;)

Thank you very much

A few things:

1) Try updating to the latest SDK (4.0.0). The FCM setup is very different now (no longer need to call connect()/disconnect(), just set shouldEstablishDirectChannel = true, FIRApp -> FirebaseApp, FIRMessaging -> Messaging, etc.)

2) Call FIRApp.configure() _before_ application.registerForRemoteNotifications(). Otherwise Firebase might not get your APNs token in order to configure your FCM token correctly for push notifications.

3) If you send data : ... that means you're sending an Apple silent notification. Silent notifications are noted by Apple as being a "low priority" notification, and there are certain cases where the silent notifications _are NOT_ delivered, until the app is opened the next time:

  • You force quit the app using the multitasking UI (swipe up to quit the app)
  • You restart your phone, and haven't opened the app yet
    In these cases, no silent notifications are delivered to any app:
  • You enable Low power mode
  • You turn off Background App Refresh in the app via Settings

If you are debugging the app, and you click "Stop" in Xcode, that quits the app, but silent notifications will continue to work, so that's a good way to test, as you can expect silent notifications to work, yet your app is not running.

Thanks for your detailed responses.
I have a php code that triggers the curl command as i have mentioned before to send FCM messages to FCM server. the android apps can handle this slient messages (which contains just data and registiration_ids) even the app is not running, and andoid can raise a push notification with sound. I am trying to do same thing in IOS,
I dont know is there any other way to send 100.000 registration_ids at the same time. I suppose that The curl command is the best but i cant add "notification" tag in the data. so it stays as a slient message for IOS.
If there is no way to display push notifications when is it not runnig while the message is slient. then I should change the notification system for app server and android app.

Hi,
I can fix it by adding notification tag and content_available as you have mentioned.
It is working now, thank you very much.

Great! Keep in mind that silent notifications (content_available) has a lower Quality of Service by Apple, so it may not be as reliable to send display notifications this way.

@ahmeric I receive the notifications when the app is in background, but the code into didRecive func is not executed. Works for you?

I check it with content_available too and setting FirebaseAppDelegateProxyEnabled to false.

Yes it works.

@available(iOS 10, *)
extension AppDelegate: UNUserNotificationCenterDelegate {  
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        let userInfo = response.notification.request.content.userInfo
        // Print message ID.
        if let messageID = userInfo[gcmMessageIDKey] {
            print("4 Message ID: \(messageID)")
            print("4 message :  \(userInfo)")
            openPostDetail(userInfo: userInfo) // my function to display post detail viewController
        }

        // Print full message.
        //        analyse(notification: userInfo)
        completionHandler()
    }
}

Hi guys
I am not getting notification in App inactive state(force fully quite by double clicking home button and swipe up).
Please help me.Thanks

@gireshIOS are you sending a notification key to fcm service?

  {
    "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification" : {
      "body" : "great match!",
      "title" : "Portugal vs. Denmark",
      "icon" : "myicon"
    }
  }

When iOS (Android and Browsers too) receive a fcm message with some notification defined, The own operative system show the notification, without excute any app code.

I read some documentation about that iOS doesn't allow execute code when notifications arrive and the app is killed, Don't wake up the app.

Sometimes the problem is that, when iOS receive this data and the app is in background (not killed), don't wake up the application and don't excute your on receive code and maybe you want to execute your code.

@rtorralba Yes I am sending notification key to fem service.
Here is my API payroll
$fields = array(
'to' => '/topics/' . $to,
'priority' => 'high',
'content_available' => true,
'notification' => $message,
);

Still I am not able get any notification from FCM when App is killed from Back ground.

@ahmeric in my project enter in this function when I click on the notification. When the notification arrive it just show and don't execute this function

I found this post in stackOverflow and for one user works in the same way:

// App in foreground
    private func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
      print("willPresent")
      }
//On Action click
     private func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
      print("didReceive")
      }

https://stackoverflow.com/questions/38954496/unusernotificationcenter-did-receive-response-with-completion-handler-is-never-c

Has this resurfaced in iOS 11? It was all working for me till I upgraded my iPhone to 11.0.3 and now I can only send notification messages and silent messages just won't arrive on the phone. Our app functionality includes performing some background task before notifying user with what has been done so a notification message which immediately display in the tray with data being processed only when user clicks the notification doesn't cut it. I guess its to move to APNs directly as I also read that data message are stored by FCM to be delivered when an active connection with app is made which doesn't help at all.

pod 'Firebase/Core'
pod 'Firebase/Messaging'
pod 'Firebase/AdMob'
pod 'Firebase/Crash'

Should we try 2.0.0 instead?

Hi @abharku there should be no changes necessary with the OS upgrade you mention. Also this issue is closed, could you open a new one with a bit more detail about the msg you are sending. Including the target (topic or token)

Thanks. I have raised a new issue and meanwhile trying to integrate with APNs directly

New issue: FCM Swift - Application does not receive message data in background - iOS 11 #360

It works for me.Thank you very much!!

I didn't receive the push notification in 10.3.3
background is not working.

@YMonnier when you posted this comment https://github.com/firebase/quickstart-ios/issues/246#issuecomment-292710316 were you getting data notifications when app was backgrounded or killed? And were you able to process the data to generate notification text to the user while app is backgrounded/killed?

@dylan-chong iOS will not deliver data notifications when the app is killed and will only deliver data notifications when the app is backgrounded if the device is not in low-power mode. iOS may also throttle data notifications to preserve the user's battery. These are iOS-specific behaviors, and are not controlled by Firebase or APNs.

If you believe you have a separate bug, please file a new issue with the version of Firebase you're using and how you've set up Messaging and we'll take a look.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bakeddean picture bakeddean  ·  6Comments

glorment picture glorment  ·  3Comments

asciiman picture asciiman  ·  7Comments

davecoffin picture davecoffin  ·  3Comments

EthanDoan picture EthanDoan  ·  6Comments