iOS与FCM问题:首次启动无法收消息,切换前后台或重启后恢复
各位Stack Overflow社区成员好!我在iOS集成FCM时遇到一个奇怪的问题:首次启动应用无法接收FCM消息,但如果关闭应用重新打开,或者把应用切换到后台再回到前台,就能突然收到之前所有的消息,之后应用运行期间接收消息都正常,只有首次启动的这个场景有异常。
目前我发现只有将FirebaseInstanceID降级到2.0.0才能解决该问题,当前使用的Firebase Pod版本如下:
- Firebase 4.13.0
- FirebaseAnalytics 4.2.0
- FirebaseAuth 4.6.1
- FirebaseCore 4.0.20
- FirebaseCrash 2.0.2
- FirebaseInstanceID 2.0.0
- FirebaseMessaging 2.2.0
使用的Xcode版本为9.3(9E145)。
我的AppDelegate的didFinishLaunchingWithOptions方法中包含以下代码:
Messaging.messaging().delegate = self Messaging.messaging().shouldEstablishDirectChannel = true FirebaseApp.configure() application.registerForRemoteNotifications()
同时实现了APNs token注册的方法:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { print("APNs token retrieved: \(deviceToken)") Messaging.messaging().apnsToken = deviceToken }
还扩展了AppDelegate遵循MessagingDelegate协议:
extension AppDelegate : MessagingDelegate { func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) { // 这里是处理FCM token的逻辑 } func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) { // 这里是接收FCM消息的逻辑 } }
请问这是FCM的常见Bug,还是我的代码实现存在错误?非常感谢!
解答
嘿,这个问题我之前在旧版本Firebase里碰到过!首先可以明确的是,这大概率是旧版本FirebaseInstanceID和Messaging配合的已知Bug,不是你的代码实现问题——毕竟你已经通过降级InstanceID解决了问题,这也侧面验证了版本兼容的问题。
具体来说,在Firebase 4.x系列的部分版本中,FirebaseInstanceID和FirebaseMessaging的初始化时序存在问题:首次启动时,FCM的直接通道(就是你设置的shouldEstablishDirectChannel = true)可能在APNs token绑定完成前就尝试建立,导致通道初始化失败,无法接收消息。而重启或者前后台切换时,APNs token已经被持久化,通道能正常建立,所以消息就都过来了。
除了降级FirebaseInstanceID到2.0.0,你还有几个可选的解决方案:
- 调整代码初始化顺序:把
FirebaseApp.configure()放在最前面,再设置delegate和直接通道,最后注册远程通知。这样能确保Firebase核心服务初始化完成后,再配置Messaging相关的参数,避免时序问题:FirebaseApp.configure() Messaging.messaging().delegate = self Messaging.messaging().shouldEstablishDirectChannel = true application.registerForRemoteNotifications() - 升级到更高版本的Firebase:Firebase在后续的版本(比如5.x及以上)已经修复了这个初始化时序的Bug,如果你能兼容更高版本的SDK,升级后这个问题应该会自动消失。不过升级前记得测试现有功能的兼容性,避免其他潜在问题。
- 手动触发通道重建:如果暂时不能升级或降级,可以在
didReceiveRegistrationToken方法里手动调用Messaging.messaging().shouldEstablishDirectChannel = true,确保在FCM token获取到后重新建立通道:func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) { // 处理token逻辑 Messaging.messaging().shouldEstablishDirectChannel = true }
另外,还要确认一下你的APNs配置是否正确——比如推送证书是否有效,App ID是否开启了推送权限,但从你重启后能正常接收消息来看,这部分应该没问题。
总的来说,这个是旧版本Firebase的已知问题,调整初始化顺序或者升级SDK都是比长期停留在旧版本更好的解决方案,毕竟旧版本可能还有其他安全或功能上的缺陷。
内容的提问来源于stack exchange,提问作者Saul Good

