Khushal Dugar
Khushal Dugar

Reputation: 231

Firebase FCM not working initially but works after 3-4 times if the app is opened

I am new to Firebase FCM Integration and i am facing some issues. When the app is installed for first time i don't receive Push Notification but if i open and close app for few times and then send push notification from Firebase i am receiving notification without any change in client or server code. Can anyone help me out on this?

I have attached my appdelegate code below

import UIKit
import Firebase
import UserNotifications
import FirebaseMessaging
import FirebaseInstanceID

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate,UNUserNotificationCenterDelegate,MessagingDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {


        FirebaseApp.configure()

        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: { (granted, error) in
                    if error == nil{
                        UIApplication.shared.registerForRemoteNotifications()
                    }
            })
        } else {
            let settings: UIUserNotificationSettings =
                UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(settings)
                application.registerForRemoteNotifications()
        }

        application.registerForRemoteNotifications()
        return true
    }

    func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) {
        print("Firebase registration token: \(fcmToken)")
    }

    func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
        UIApplication.shared.registerForRemoteNotifications()
    }

    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        // I have tried both method but none worked and i also tried MessagingAPNSTokenType.sandbox and prod
        // Method 1: Messaging.messaging().apnsToken = deviceToken
        // Method 2:
        Messaging.messaging().setAPNSToken(deviceToken, type: MessagingAPNSTokenType.unknown)
    }
}

Thanks in advance

Upvotes: 2

Views: 2621

Answers (4)

Sachin Kishore
Sachin Kishore

Reputation: 324

NotificationCenter.default.addObserver(self selector:#selector(self.getFcmToken), name: .firInstanceIDTokenRefresh, object: nil)

put this in application did finsih launching method and make a function

func getFcmToken(notification: NSNotification) {

    guard let contents = FIRInstanceID.instanceID().token()
        else {
            return
    }
    print("InstanceID token: \(contents)")
    if let token = FIRInstanceID.instanceID().token(){
        print(token)

}

Upvotes: 0

Rashwan L
Rashwan L

Reputation: 38833

I have the same issue as you have and there is a known issue where the FCM token is not always being associated with the APNs device token if UIApplication.shared.registerForRemoteNotifications() is not called early enough.

According to this GitHub thread this issue has already been fixed in the FirebaseInstanceID SDK, and should be coming out soon.

In the meantime, you can:

Lock your FirebaseInstanceID pod to 2.0.0 in your Podfile, or

Ensure you're calling UIApplication.shared.registerForRemoteNotifications() on app start, ideally before FirebaseApp.configure().

Update:
I just made some additional tests and I noticed that this is a bit random of when this will work. For some devices it worked immediately for some not. For some devices Single Token push worked and not User Segment. I managed to make it work so that you at least either get Single Token push or User Segment by adding the following:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    if #available(iOS 10.0, *) {
        let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
        UNUserNotificationCenter.current().requestAuthorization(
            options: authOptions,
            completionHandler: {_, _ in
        })

        // For iOS 10 display notification (sent via APNS)
        UNUserNotificationCenter.current().delegate = self
        // For iOS 10 data message (sent via FCM)
        Messaging.messaging().delegate = self
        application.registerForRemoteNotifications()
        print("::: registerForRemoteNotifications iOS 10")

    } else {
        let settings: UIUserNotificationSettings =
            UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
        application.registerUserNotificationSettings(settings)
        application.registerForRemoteNotifications()
        print("::: registerUserNotificationSettings iOS 9")
    }

    FirebaseApp.configure()
    Messaging.messaging().delegate = self
    Messaging.messaging().shouldEstablishDirectChannel = true

    if let refreshedToken = InstanceID.instanceID().token() {
        print("::: InstanceID token: \(refreshedToken)")
    }

    NotificationCenter.default.addObserver(self, selector: #selector(tokenRefreshNotification), name: NSNotification.Name.InstanceIDTokenRefresh, object: nil)
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Messaging.messaging().setAPNSToken(deviceToken, type: .prod)

    if let refreshedToken = InstanceID.instanceID().token() {
        print("InstanceID token: \(refreshedToken)")
    }
}

The thing is that it won´t work if you don´t access the didRegisterForRemoteNotificationsWithDeviceToken function.

Upvotes: 3

Yuyutsu
Yuyutsu

Reputation: 2527

Update your appdelegate by this

import FirebaseCore
import FirebaseInstanceID
import FirebaseMessaging
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate{

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        self.fcmInitialSetup(application)
        return true
    }
    func fcmInitialSetup(_ application: UIApplication){

        // [START register_for_notifications]
        if #available(iOS 10.0, *) {
            let uns: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(uns)
            application.registerForRemoteNotifications()

        } else {
            let settings: UIUserNotificationSettings =
                UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
            application.registerUserNotificationSettings(settings)
        }

        application.registerForRemoteNotifications()

        // [END register_for_notifications]

        FIRApp.configure()

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

        if let token = FIRInstanceID.instanceID().token() {
            sendTokenToServer(token)
        }
    }

    func sendTokenToServer(_ currentToken: String) {
        print("sendTokenToServer() Token: \(currentToken)")
        // Send token to server ONLY IF NECESSARY

        print("InstanceID token: \(currentToken)")
        self.token = currentToken
        UserDefaults.standard.set(self.token, forKey: "token")
        UserDefaults.standard.synchronize()
        if self.token != nil{
            let userInfo = ["token": self.token]
            NotificationCenter.default.post(
                name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
        }
    }


    // NOTE: Need to use this when swizzling is disabled
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let tokenChars = (deviceToken as NSData).bytes.bindMemory(to: CChar.self, capacity: deviceToken.count)
        var tokenString = ""

        for i in 0..<deviceToken.count {
            tokenString += String(format: "%02.2hhx", arguments: [tokenChars[i]])
        }

        FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.unknown)
        print("Device Token:", tokenString)
        print("FIRInstanceID.instanceID().token() Token:", FIRInstanceID.instanceID().token())
        if let tokenData = FIRInstanceID.instanceID().token(){
            UserDefaults.standard.set(tokenData, forKey: "token")
            UserDefaults.standard.synchronize()
            let userInfo = ["token": tokenData]
        }
    }

    func tokenRefreshNotification(_ notification: Notification) {
        // NOTE: It can be nil here
        //        print("Token:\(FIRInstanceID.instanceID().token()!)")
        if let refreshedToken = FIRInstanceID.instanceID().token() {
            print("InstanceID token: \(refreshedToken)")
            UserDefaults.standard.set(refreshedToken, forKey: "token")
            UserDefaults.standard.synchronize()
            print("update now \(self.token)")
            if self.token != nil{
                let userInfo = ["token": self.token]
                NotificationCenter.default.post(
                    name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
            }

        }

        // Connect to FCM since connection may have failed when attempted before having a token.
        connectToFcm()
    }
    // [END refresh_token]
    func connectToFcm() {
        FIRMessaging.messaging().connect { (error) in
            if (error != nil) {
                print("Unable to connect with FCM. \(error)")
            } else {
                print("Connected to FCM.")
            }
        }
    }

    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
        print(userInfo)
    }

    func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
        print("Within open URL")
        return true
    }


    // [START receive_apns_token_error]
    func application( _ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError
        error: Error ) {
        print("Registration for remote notification failed with error: \(error.localizedDescription)")
        // [END receive_apns_token_error]
        let userInfo = ["error": error.localizedDescription]
        NotificationCenter.default.post(
            name: Notification.Name(rawValue: rkey), object: nil, userInfo: userInfo)
    }

    func registrationHandler(_ token: String!, error: NSError!) {
        if (token != nil) {
            self.token = token!
            print("Registration Token: \(self.token)")
            UserDefaults.standard.set(self.token, forKey: "token")
            UserDefaults.standard.synchronize()
            let userInfo = ["token": self.token]
            NotificationCenter.default.post(
                name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
        } else {
            print("Registration to GCM failed with error: \(error.localizedDescription)")
            let userInfo = ["error": error.localizedDescription]
            NotificationCenter.default.post(
                name: Notification.Name(rawValue: self.rkey), object: nil, userInfo: userInfo)
        }
    }

    func registerForPushNotifications(_ application: UIApplication) {
        let notificationSettings = UIUserNotificationSettings(
            types: [.badge, .sound, .alert], categories: nil)
        application.registerUserNotificationSettings(notificationSettings)
    }

    func application(_ application: UIApplication, didRegister notificationSettings: UIUserNotificationSettings) {
        if notificationSettings.types != UIUserNotificationType() {
            application.registerForRemoteNotifications()
        }
    }

    // [START receive_message]
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (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. add Toast
        print(userInfo);

        print(application.keyWindow?.visibleViewController() ?? "")

        print("Message ID: \(userInfo["gcm.message_id"]!)")


        // Print full message.
        print("%@", userInfo)
    }
    // [END receive_message]



    func applicationDidBecomeActive(_ application: UIApplication) {
        connectToFcm()
    }

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

    func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

    }
}
// [END disconnect_from_fcm]


// [START ios_10_message_handling]
@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) {
        let userInfo = notification.request.content.userInfo
        // Print message ID.
        print("Message ID: \(userInfo["gcm.message_id"]!)")
        // Print message ID. add Toast

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

extension AppDelegate : FIRMessagingDelegate {
    // Receive data message on iOS 10 devices.
    func applicationReceivedRemoteMessage(_ remoteMessage: FIRMessagingRemoteMessage) {
        print("%@", remoteMessage.appData)
    }
}

Upvotes: 0

Riajur Rahman
Riajur Rahman

Reputation: 2034

Firebase FCM, it takes sometime to generate its first token. After getting this token you will start receiving push notification. Check these out. It will be clear for sure:

  1. Check this answer
  2. AppDelegate implementation with FCM

Hope this helps.

Upvotes: 0

Related Questions