为 FCM 检索令牌的正确方法 - iOS 10 Swift 3

Posted

技术标签:

【中文标题】为 FCM 检索令牌的正确方法 - iOS 10 Swift 3【英文标题】:Correct way to retrieve token for FCM - iOS 10 Swift 3 【发布时间】:2017-02-22 03:36:44 【问题描述】:

我已经使用 FirebaseAuth/FCM 等实现了 Firebase,并通过 Firebase 控制台成功发送了通知。

但是我需要从我自己的应用服务器推送通知。

我想知道下面哪种方式是检索设备注册 ID 的正确方式:-

1) 从 didRegisterForRemoteNotificationWithDeviceToken 检索注册 id 令牌

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) 
    var token = ""

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

    print("Registration succeeded!")
    print("Token: ", token)
    Callquery(token)


2) 从 firebase 检索注册令牌(基于检索当前注册令牌的 Firebase 文档)

let token = FIRInstanceID.instanceID().token()!

我使用的是第一种方式,即使注册 ID 相应地存储在我的应用服务器数据库中,也没有收到推送通知,我得到了这个 CURL 会话结果:-

"multicast_id":6074293608087656831,"success":0,"failure":1,"canonical_ids":0,"results":["error":"InvalidRegistration"]

我也尝试过第二种方法,在运行应用程序时出现致命错误,如下所示:-

如果有人能指出正确的方法,不胜感激,谢谢!

【问题讨论】:

看到这个对你有帮助***.com/questions/37667753/… 注意:didRegisterForRemoteNotificationWithDeviceToken 中的 deviceToken 参数不是 Firebase 注册令牌。您仍然需要调用 return InstanceID.instanceID().token() ,如下所述。 【参考方案1】:

首先导入如下库:

import FirebaseInstanceID
import FirebaseMessaging
import UserNotifications

设置委托:MessagingDelegate、UNUserNotificationCenterDelegate

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

在 didFinishLaunching() 上写下这段代码:

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool 
    
    // Override point for customization after application launch.
    FirebaseApp.configure()
    Messaging.messaging().delegate = self
    
    //remote Notifications
    if #available(ios 10.0, *) 
        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge])  (isGranted, err) in
            if err != nil 
                //Something bad happend
             else 
                UNUserNotificationCenter.current().delegate = self
                Messaging.messaging().delegate = self
                
                DispatchQueue.main.async 
                    UIApplication.shared.registerForRemoteNotifications()
                
            
        
     else 
        // Fallback on earlier versions
    
    
    if #available(iOS 10, *) 
        UNUserNotificationCenter.current().requestAuthorization(options: [.badge,.sound,.alert], completionHandler:  (granted, error) in
            application.registerForRemoteNotifications()
        )
    else
        let notificationSettings = UIUserNotificationSettings(types: [.badge,.sound,.alert], categories: nil)
        UIApplication.shared.registerUserNotificationSettings(notificationSettings)
        UIApplication.shared.registerForRemoteNotifications()
    
    
    return true

这样写connectFCM方法:

func ConnectToFCM() 
    Messaging.messaging().shouldEstablishDirectChannel = true
    
    if let token = InstanceID.instanceID().token() 
        print("\n\n\n\n\n\n\n\n\n\n ====== TOKEN DCS: " + token)
    

还要编写委托方法来注册和接收推送通知:

func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) 
    print("\n\n\n\n\n ==== FCM Token:  ",fcmToken)
    HelperFunction.helper.storeInUserDefaultForKey(name: kFCMToken, val: fcmToken)
    ConnectToFCM()


@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) 
    
   // UIApplication.shared.applicationIconBadgeNumber += 1
    
    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "Barker"), object: nil)


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


func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) 
    
    print(userInfo)
    
    completionHandler(UIBackgroundFetchResult.newData)

        

现在我们可以从 firebase 控制台对其进行测试。

100% 工作、简单且经过测试

注意:

    从 xcode 的功能部分启用推送通知。

    检查您在 firebase 项目设置上上传的两个 p12 证书。

    设备令牌只能从真实设备而不是模拟器获取。

【讨论】:

【参考方案2】:

这个问题很老,但如果有人想在 Objective C 中使用。

最新的 Firebase:6.27.0

在 iOS Objective C 中我们可以这样使用

[[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult * _Nullable result,
                                                            NSError * _Nullable error) 
            if (error != nil) 
                NSLog(@"Error : %@", error);
             else 
                token = result.token;
            
            NSLog(@"Token %@", result.token);
           
        ];

并获取实例 ID:

 [[FIRInstanceID instanceID] getIDWithHandler:^(NSString *identity, NSError *error) 
         if (error != nil) 
           NSLog(@"Error : %@", error);
          else 
           NSLog(@"instance ID: %@", identity);
         
         NSLog(@"IID %@", identity);
       ];

【讨论】:

【参考方案3】:

确认 MessagingDelegate 协议。

然后您可以添加以下委托方法并获取 Firebase 令牌。 (文档https://firebase.google.com/docs/cloud-messaging/ios/client)

func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) 
        // send to remote server
        InstanceID.instanceID().instanceID  result, error in
            if let error = error 
                print("Error fetching remote instance ID: \(error)")
             else if let result = result 
                print("Remote instance ID token: \(result.token)")
            
        
    

【讨论】:

【参考方案4】:

首先,你应该导入所有需要的库

import Firebase
import UserNotifications

稍后在 AppDelegate.swift 中调用下一个函数

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) 
    Messaging.messaging().apnsToken = deviceToken

    InstanceID.instanceID().instanceID  (result, error) in
        if let error = error 
            print("Error fetching remote instance ID: \(error)")
         else if let result = result 
            print("Remote instance ID token: \(result.token)")
        
    

【讨论】:

【参考方案5】:

tokenRefreshNotification 函数在启动应用时并不总是被调用。

但是,将代码放在常规的didRegisterForRemoteNotificationsWithDeviceToken 委托函数中时,我每次都可以获得令牌:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) 
    if let refreshedToken = InstanceID.instanceID().token() 
        print("InstanceID token: \(refreshedToken)")
    

(Swift 3 + Firebase 4.0.4)

【讨论】:

嗨,如何发布请求令牌? 如果有人做了我所做的,deviceToken:Data 参数不是 FCM 注册令牌。来自 InstanceID().token() 的令牌是。 我得到 'token()' 已被弃用:使用 instanceIDWithHandler: 代替。对于令牌() 我这样做了:InstanceID.instanceID().instanceID (result, error) in if error == nil print("InstanceID token: (result)") else print(error , ": 获取 Fire 令牌时出错") this 怎么样?注册令牌通过messaging:didReceiveRegistrationToken: 方法传递。此方法通常在每个应用程序以注册令牌开始时调用一次。【参考方案6】:

我遇到了同样的问题,但不知道发生了什么。

@Sam 建议的 didRegisterForRemoteNotificationsWithDeviceToken 每次都(几乎)被调用,所以这是一个很好的解决方法。但是,当您第一次使用刷新的令牌打开应用程序时,它不会被调用。

因此,对于这种情况,您仍然需要:

func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) 
    print("Refreshed Token: \(fcmToken)")

所以如果你只使用:

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) 
    if let fcmToken = InstanceID.instanceID().token() 
        print("InstanceID token: \(fcmToken)")
    

您只会在用户第二次打开应用时获得“刷新令牌”。

我设法通过卸载应用程序并清理构建文件夹(产品 > 清理构建文件夹)来强制刷新令牌。适合测试。

理想情况下,这一切都可以在messaging:didReceiveRegistrationToken 委托方法中处理,但我无法使其工作。另一种获取 FCM 令牌更改通知的方法是按照文档中的建议收听名为 kFIRMessagingRegistrationTokenRefreshNotificationNSNotification:https://firebase.google.com/docs/cloud-messaging/ios/client

【讨论】:

【参考方案7】:

获取当前的 FCM Token

if let token = Messaging.messaging().fcmToken 
    // token is current fcmToken

更新当前的 FCM 令牌

如果我们删除当前的instanceId,稍后将通过vi MessagingDelegate (messaging:didReceiveRegistrationToken) 接收到新的token。

InstanceID.instanceID().deleteID  (error) in
    if let er = error 
        print(er.localizedDescription)
     else 
        print("instanceID().deleteID  success ---------------➤")
    

【讨论】:

【参考方案8】:

Firebase 推荐的方式:

let token = Messaging.messaging().fcmToken

参考:Setting Up a Firebase Cloud Messaging Client App on iOS

【讨论】:

【参考方案9】:

Swift 4 + Firebase (5.3.0)

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) 
    InstanceID.instanceID().instanceID(handler:  (result, error) in
        if let error = error 
            print("Error fetching remote instange ID: \(error)")
         else if let result = result 
            print("Remote instance ID token: \(result.token)")
        
    )

【讨论】:

【参考方案10】:

斯威夫特 4

礼貌:https://***.com/a/50945350/1014164

InstanceID.instanceID().instanceID  (result, error) in
    if let error = error 
        print("Error fetching remote instange ID: \(error)")
     else if let result = result 
        print("Remote instance ID token: \(result.token)")
    

【讨论】:

我在主屏幕 viewdidload 中调用时得到“静态成员 'instanceID' 不能使用”,你能帮忙吗? @kemdo 确保你用括号写 InstanceID.instanceID().instanceID 而不是 InstanceID.instanceID.instanceID @kemdo,Yocoh,当我遇到静态成员 'instanceID...... 问题时,你是如何解决这个问题的?【参考方案11】:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)         
    Messaging.messaging().apnsToken = deviceToken

    let deviceTokenString = deviceToken.reduce("")  $0 + String(format: "%02X", $1) 

    print("APNs device token: \(deviceTokenString)"

【讨论】:

这是做什么的?你能解释一下吗?【参考方案12】:

首先注册firebase令牌刷新通知:

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

然后就可以在tokenRefreshNotification选择器中接收token了:

func tokenRefreshNotification(_ notification: Notification) 
    if let refreshedToken = FIRInstanceID.instanceID().token() 
      print("InstanceID token: \(refreshedToken)")
    

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

【讨论】:

请注意,InstanceIDTokenRefreshFIRInstanceID 现在已弃用。【参考方案13】:

Swift 3 + Firebase 4.0.4:

static var FirebaseToken : String? 
    return InstanceID.instanceID().token()

【讨论】:

只是为了澄清;你的和下面的答案(Messaging.messaging().fcmToken)有什么区别? @MBH ***.com/questions/44615817/…【参考方案14】:

FCM 设备令牌 swift3

    let fcmDeviceToken = FIRInstanceID.instanceID().token()
    print("FCM token: \(fcmDeviceToken ?? "")")

【讨论】:

【参考方案15】:

使用第二个选项,这看起来真的很愚蠢/简单,但要修复那个 nil 可选的致命错误,只需删除最后的 force-unwrap

您的代码:var token = FIRInstanceID.instanceID().token()! 成功:var token = FIRInstanceID.instanceID().token()

这至少可以解决令人讨厌的崩溃

【讨论】:

嗨约翰,错误仍然存​​在,似乎 FIRInstanceID.instanceID().token() 返回 nil。 @aznelite89,之所以为nil,是因为你还没有注册Firebase Cloud Messaging

以上是关于为 FCM 检索令牌的正确方法 - iOS 10 Swift 3的主要内容,如果未能解决你的问题,请参考以下文章

在Android中获取FCM消息令牌的正确方法是啥?

使用离子电容器的 FCM 推送通知在 IOS 中不起作用

FCM/APNS 集成适用于新令牌,但在转换旧 APNS 令牌时不行?

Android APP 从 GCM 迁移到 FCM。旧 GCM 令牌不起作用

提供的注册令牌未注册

突然 fcm 通知在某些 iOS 设备上不起作用