Firebase FCM 最初不工作,但如果应用程序打开,则在 3-4 次后工作

Posted

技术标签:

【中文标题】Firebase FCM 最初不工作,但如果应用程序打开,则在 3-4 次后工作【英文标题】:Firebase FCM not working initially but works after 3-4 times if the app is opened 【发布时间】:2017-10-05 05:39:29 【问题描述】:

我是 Firebase FCM 集成的新手,我遇到了一些问题。首次安装应用程序时,我没有收到推送通知,但如果我打开和关闭应用程序几次,然后从 Firebase 发送推送通知,我将收到通知,而客户端或服务器代码没有任何更改。谁能帮我解决这个问题?

我在下面附上了我的 appdelegate 代码

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)
    

提前致谢

【问题讨论】:

【参考方案1】:

我遇到了与您相同的问题,并且存在一个已知问题,即如果没有足够早地调用 UIApplication.shared.registerForRemoteNotifications()FCM token 并不总是与 APNs device token 相关联。

根据this GitHub 线程,这个问题已经在FirebaseInstanceID SDK 中修复,应该很快就会出来。

与此同时,您可以:

将您的FirebaseInstanceID pod to 2.0.0 锁定在您的 Podfile 中,或者

确保您在应用启动时调用UIApplication.shared.registerForRemoteNotifications(),最好在FirebaseApp.configure() 之前调用。

更新: 我刚刚做了一些额外的测试,我注意到这有点随机。对于某些设备,它会立即起作用,而对于某些设备则不能。对于某些设备Single Token push 有效,而不是User Segment。我设法使它工作,以便您至少通过添加以下内容获得Single Token pushUser Segment

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)")
    

问题是,如果您不访问 didRegisterForRemoteNotificationsWithDeviceToken 函数,它将无法工作。

【讨论】:

我没有这样的吊舱。这些是我用过的唯一豆荚。 pod 'Firebase/Core' pod 'Firebase/Messaging' 请帮助 感谢您的回答,但我发现即使使用您的代码,如果通过针对所有 iOS 设备模式发送,我也不会收到来自 Firebase 的通知,但是,如果我使用 FCM 令牌将其发送到特定的它正在工作的注册设备。任何想法为什么会出现这种行为? 是的,我得到了相同的结果,但一段时间后它也适用于所有设备。我认为这将在 iOS 11 中修复,所以请稍等,它可能很快就会起作用。【参考方案2】:

Firebase FCM,生成第一个令牌需要一些时间。获得此令牌后,您将开始接收推送通知。检查这些。肯定会很清楚:

    Check this answer AppDelegate implementation with FCM

希望这会有所帮助。

【讨论】:

【参考方案3】:

通过此更新您的 appdelegate

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)
    

【讨论】:

【参考方案4】:

NotificationCenter.default.addObserver(self selector:#selector(self.getFcmToken), name: .firInstanceIDTokenRefresh, 对象:无)

把这个放在应用程序做finsih启动方法 并制作一个函数

func getFcmToken(notification: NSNotification)

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

【讨论】:

以上是关于Firebase FCM 最初不工作,但如果应用程序打开,则在 3-4 次后工作的主要内容,如果未能解决你的问题,请参考以下文章

Firebase 通知无法使用 FCM 服务正常工作

ANGULAR Firebase FCM 发送成功但不可见

Flutter FCM 推送通知不起作用

使用 FCM 推送通知不工作 iOS

Firebase FCM 推送通知停止工作 iOS 11.1.1

IOS 应用程序中的 Firebase 云消息传递 (FCM) 无法正常工作