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

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

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 2
  • Comments: 25 (4 by maintainers)

Most upvoted 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,

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.

@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?

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,