如何检测真正的 iOS/APNS 推送令牌何时注册到 Firebase 云消息传递 (FCM)?

How can I detect when the real iOS/APNS push token is registered with Firebase Cloud Messaging (FCM)?

即使用户没有授予推送通知权限,Firebase 的 didReceiveRegistrationToken 也会被调用,如下所示。我需要确保正在注册 APNS 推送令牌以进行分析,并将其保存在我的服务器上,但即使用户未授予推送权限,也会调用此函数。 ‍♂️

/**
 * Requirement for Firebase push notifications.
 * See documentation here: https://firebase.google.com/docs/cloud-messaging/ios/client
 */
extension AppDelegate: MessagingDelegate {
    // Note: This callback is fired at each app startup and whenever a new token is generated.
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
        print("Firebase registration token: \(fcmToken)")
        let dataDict: [String: String] = ["token": fcmToken]
        NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
        print("Getting called even if the user hasn't opted in for push notifications!")
    }
}

获取设备 ID 令牌的回调和获取实际推送消息的回调是两件不同的事情。设备 ID 令牌回调只是为您提供一个令牌,用于标识稍后推送通知的设备。它实际上并不是推送通知本身。只是一个ID。

也就是说,如果您从安全角度不同意客户端 SDK 的行为,您可以contact Firebase support directly 提出反馈。

TL;DR:FCM 令牌不是检测 APNS 令牌是否已注册的可靠方法。 Disable method swizzling in firebase and listen to APNS registration manually.

  1. FCM 令牌: FCM 令牌也称为设备实例 ID;它标识该特定设备上的特定应用程序。换句话说,FCM 令牌不等于 APNS 令牌。 这是一种解释:APNs, FCM or GCM token

  2. FCM 令牌自动生成: 令人惊讶的是,即使没有 iOS 用户授予权限,Firebase 也会在应用启动时生成一个 FCM 令牌。我猜它想找到一种方法来识别应用程序和设备对,所以一旦启动,您就会拥有一个 FCM 令牌。如果需要,您可以 disable auto-generation 并等待用户选择加入。

By default, the FCM SDK generates a registration token for the client app instance on app launch. If you want to get an explicit opt-in before using Instance ID, you can prevent generation at configure time by disabling FCM. To do this, add a metadata value to your Info.plist (not your GoogleService-Info.plist):

问题就出在这里! 即使用户禁用了自动生成 FCM 令牌,当系统提示用户启用推送通知时也会生成一个,而不是在用户已接受推送通知权限时生成。 这看起来很奇怪我最终将此报告给了 Firebase。

  1. APNS Token Swizzling:因此默认情况下 Firebase 接管 AppDelegate 的 didRegisterForRemoteNotificationsWithDeviceToken(称为“swizzling”)。您可以禁用它并自己覆盖委托方法。 这将使您能够访问 APNS 令牌和 FCM 令牌。
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    print("Successfully registered for notifications!")
    // Required to make FCM work since we disabled swizzling!
    Messaging.messaging().apnsToken = deviceToken
    // String version of APNS so we can use save it/use it later/do analytics.
    let apnsToken = deviceToken.map { String(format: "%02.2hhx", [=10=]) }.joined()
}