如果令牌更改,则无法通过令牌接收发送到设备的测试通知,ios与swift

Posted

技术标签:

【中文标题】如果令牌更改,则无法通过令牌接收发送到设备的测试通知,ios与swift【英文标题】:Can't receive sent test notification to device by token if the token changes, ios with swift 【发布时间】:2019-10-30 16:10:15 【问题描述】:

是的,这里已经有很多相同但不同的解决方案,但没有一个对我有帮助。 Firebase 的逻辑对它似乎能够做的一切都是压倒性的,但关于“如何做”的信息仍然是旧的,并且并不适用于最新的。我遇到的问题是我希望每次都收到从 Firebase 控制台发送到我的手机的测试通知,而不仅仅是在第一个安装的应用程序的令牌上。一旦我在手机上卸载应用程序并使用新令牌运行新应用程序,即使我在从 Firebase 控制台发送时使用新令牌,测试通知也无法到达我的手机。

这就是我一直在尝试做的事情..

在 Firebase 控制台中,我添加了一个项目并将其命名为“测试”,无需分析。

打开我的 Xcode V11.1 并创建一个全新的 ios 单视图应用程序项目并输入产品名称:“Testing” with my Team 等。 选择语言:“Swift”和用户界面:“Storyboard”。

在“Signing & Capabilities”文件夹下的项目目标“Testing”中,我按下加号并添加了“Push Notifications” 以防万一我还添加了“后台模式”并选中了“远程通知”。

在我的 xcode 测试项目中,我现在将以下代码添加到我的 AppDelegate.swift 文件中。

import Firebase
import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate 

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 
        // Override point for customization after application launch.
        setupPushNotificationsHandling(application)
        return true
    

    private func setupPushNotificationsHandling(_ application:     UIApplication) 
          FirebaseApp.configure()

          application.registerForRemoteNotifications()

          UNUserNotificationCenter.current().delegate = self
              UNUserNotificationCenter.current().requestAuthorization(options:     [.alert, .sound])  (_, _) in 
      

      func application(_ application: UIApplication,     didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) 
          // Receive notifications from the "all" topic
          subscribeToNotificationsTopic(topic: "all")

      

      func subscribeToNotificationsTopic(topic: String) 
          // Retry until the notifications subscription is successful
          DispatchQueue.global().async 
              var subscribed = false
              while !subscribed 
                      let semaphore = DispatchSemaphore(value: 0)

                  InstanceID.instanceID().instanceID  (result, error) in
                      if let result = result 
                          // Device token can be used to send notifications exclusively to this device
                          print("Device token \(result.token)")

                          // Subscribe
                          /*Messaging.messaging().subscribe(toTopic: topic)*/

                          // Notify semaphore
                          subscribed = true
                          semaphore.signal()
                      
                  

                  // Set a 3 seconds timeout
                  let dispatchTime = DispatchTime.now() + DispatchTimeInterval.seconds(3)
              _ = semaphore.wait(timeout: dispatchTime)
              
          
      
    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration 
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) 
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    

    // MARK: - Core Data stack

    lazy var persistentContainer: NSPersistentContainer = 
        /*
         The persistent container for the application. This implementation
         creates and returns a container, having loaded the store for the
         application to it. This property is optional since there are legitimate
         error conditions that could cause the creation of the store to fail.
        */
        let container = NSPersistentContainer(name: "Testing")
        container.loadPersistentStores(completionHandler:  (storeDescription, error) in
            if let error = error as NSError? 
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

                /*
                 Typical reasons for an error here include:
                 * The parent directory does not exist, cannot be created, or disallows writing.
                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                 * The device is out of space.
                 * The store could not be migrated to the current model version.
                 Check the error message to determine what the actual problem was.
                 */
                fatalError("Unresolved error \(error), \(error.userInfo)")
            
        )
        return container
    ()

    // MARK: - Core Data Saving support

    func saveContext () 
        let context = persistentContainer.viewContext
        if context.hasChanges 
            do 
                try context.save()
             catch 
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            
        
    


此代码 sn-p 不会以非静默方式或静默方式接收任何通知,但我目前对此不感兴趣。现在我只想接收由 Firebase API 自动处理的简单通知。 而且我还评论了以下行!

/*Messaging.messaging().subscribe(toTopic: topic)*/

因为我还没有使用订阅部分。

回到我的测试 Firebase 项目并添加了 ios 应用。 输入从 xcode 项目中获取的 IOS Bundle ID 并按下“Register App”。

下载了 GoogleService-Info.plist 并将其安装在我的 xcode 测试项目文件夹中。

在我的 Mac 上打开一个终端窗口并转到我的 xcode“测试”应用程序文件夹并输入..

pod init

打开新创建的 Pod 文件并添加如下所示的 pod。

target 'Testing' do
use_frameworks!
pod 'Firebase/Messaging'
end

保存更改并在终端窗口中键入命令。

pod install

通过打开 XCode 测试项目的 pod 命令打开新创建的 xcode 工作区文件。

运行代码并接受通知提示。 它写了一个设备令牌,我打印了一个令牌,但我也收到了以下警告。

[Firebase/Messaging][I-FCM001000] FIRMessaging 远程通知代理已启用,将调动远程通知接收器处理程序。如果您希望手动集成 Firebase 消息传递,请将“FirebaseAppDelegateProxyEnabled”添加到您的 Info.plist,并将其设置为 NO。

所以我在 info.plist 文件中添加了“FirebaseAppDelegateProxyEnabled”和“NO”。

然后我清理了构建文件夹并再次运行它。这次没有 Swizzle 警告,但只有设备令牌写入日志。

复制令牌并转到 Firebase 控制台发送测试通知并添加令牌,在标题和文本中写入一些愚蠢的数据,然后按“测试”。什么都没发生,我的手机没有收到自动通知!?

所以我转到 Apple 开发者控制台并注意到在“标识符”下已经创建了一个用于测试的,至少不是由我直接创建的。

这里我开始感到有点头晕,不知道该怎么办..

我的头晕让我顺其自然,并在 Apple Developer Console 的“Keys”下添加了一个键。将其命名为“Firebase Testing Dev”并检查“Apple Push Notifications service (APNs)”并通过单击按钮进行注册。

已将密钥下载到我的 mac。

回到设置“云消息”下的 Firebase 项目“测试”,并在 ios 应用的“APNs 身份验证密钥”下上传密钥。在那里,我添加了从 Apple Developer Console 获得的密钥 ID 和团队 ID。

我像一匹快乐的马一样跑回我的 XCode 项目“测试”并重新构建它,清除构建文件夹并按下播放标志。 而且我得到了和上一个相同的 Token,因为我没有在两次构建之间从手机上卸载应用程序。

我拿了令牌并再次尝试在 Firebase 控制台中发送测试通知。什么也没发生,注意到我的应用程序在前台并确实将其置于后台.. 然后我收到了我从 Firebase 控制台发送的第一个幸运通知!

这次我想从我的手机中删除该应用程序,然后在 XCode 中使用 play 登录重新安装它,所以我做了并且确实得到了一个新的 Token。

转到 Firebase 控制台并测试以使用新令牌发送新的测试通知,但没有任何反应! 有点不对劲。

为什么会崩溃,因为我从手机上卸载了该应用程序只是为了再次安装同一个捆绑的应用程序并获得新令牌。

怎么了?我做错了什么?

顺便说一句,是的,我已经在真实设备上运行过它。

【问题讨论】:

题中有很多数据和代码;如何接收通知在文档Setting up messaging 中有详细介绍。我自己经历过它(很多次大声笑)并且数据是最新的,按照指南构建和运行项目 - 通知从控制台发送并由客户端接收。您是否单步执行了代码以确保以正确的顺序调用所有函数?您可能希望将其减少到入门指南中显示的内容。 我没有处理 firebase 通知,但苹果文档告诉您“使用 Apple 提供的 API 注册您的应用并每次启动应用时接收您的设备令牌。 " 所以基本上不是对抗令牌更改,而是将其视为不可避免,并在应用启动时运行注册过程 @Jay 我确实读过了,但它没有说明为什么如果令牌更改它会停止工作。 您应该监控您的委托中的令牌更改,然后在它们发生时处理它们。我在您的问题中没有看到任何这样做的代码。此外,如果您禁用了 Swizzling,则需要将您的 APNs 令牌映射到 FCM 注册令牌。如果您错过了,指南中将涵盖所有内容。 好的,我现在可以正常工作了,但我不能让静默消息进入,只有当应用程序处于前台时。在早期的项目中,我让他们这样做。它刚刚停止工作。 【参考方案1】:

https://github.com/firebase/firebase-ios-sdk/issues/2438 that 中记录的适用于 iOS 的 Firebase 5.16、5.17、5.18 和 5.19 中存在一个错误,与您的问题非常相似。

您可以尝试降级到 Firebase 5.15 或尝试解决方法。经过几天的工作和解决其他问题后,我们创建了这个简单的两步解决方法

    不要相信 Firebase 检索到的最后一个令牌除非... “didRegisterForRemoteNotificationsWithDeviceToken”方法在 Firebase 检索新令牌几秒钟后触发(因此您可以信任 Firebase 令牌)。

在调用 InstanceID(第一名)和“didRegisterForRemoteNotificationsWithDeviceToken”(第二名)的代码中使用一些流控制指令(如“if”)来实现这一点非常容易。

不要忘记将 InstanceID 与 shouldStablishDirectChannel 一起使用

(后面的代码示例...)

【讨论】:

以上是关于如果令牌更改,则无法通过令牌接收发送到设备的测试通知,ios与swift的主要内容,如果未能解决你的问题,请参考以下文章

是否需要在FCM中修剪设备?

如何在 aws sns 中注册 iOS 设备令牌以接收推送通知?

无法收到ios通知

iOS APN 推送通知 - 设备令牌

从 Apple APNS 接收设备令牌所需的时间

错误:通过 iTunes 加载的应用程序推送通知的令牌无效