App启动时Firestore无法保存FCM Token的问题咨询
解决FCM Token首次启动无法保存到Firestore的问题
问题根源
首次启动App时,FCM Token的生成是异步操作,在application(_:didFinishLaunchingWithOptions:)中直接读取Messaging.messaging().fcmToken大概率会拿到nil,自然无法触发保存逻辑。而重启App后,Token已经被本地缓存,此时能读取到有效值,所以保存成功。另外你虽然实现了MessagingDelegate,但didReceiveRegistrationToken方法是空的,这个方法才是FCM Token生成或更新时的官方回调入口。
修复方案
- 删除
didFinishLaunchingWithOptions中直接读取fcmToken并保存的代码段 - 在
MessagingDelegate的didReceiveRegistrationToken方法中处理Token的保存逻辑,确保Token生成后立即触发保存 - 优化
saveFCMToken方法,无需先查询文档再判断,直接用merge: true参数实现新增/更新Token的逻辑,简化代码
修改后的完整代码
import UIKit import Firebase import FirebaseMessaging import UserNotifications class AppDelegate: NSObject, UIApplicationDelegate { let gcmMessageIDKey = "gcm.message_id" func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { FirebaseApp.configure() Messaging.messaging().delegate = self if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: { _, _ in }) } else { let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil) application.registerUserNotificationSettings(settings) } application.registerForRemoteNotifications() return true } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { if let messageID = userInfo[gcmMessageIDKey] { print("Message ID: \(messageID)") } print(userInfo) completionHandler(UIBackgroundFetchResult.newData) } private func saveFCMToken(_ deviceToken: String) { guard let uid = FirebaseManager.shared.auth.currentUser?.uid else { print("用户未登录,无法保存FCM Token") return } FirebaseManager.shared.firestore.collection("users").document(uid).setData(["token": deviceToken], merge: true) { error in if let error = error { print("保存FCM Token失败: \(error.localizedDescription)") return } print("FCM Token已成功保存/更新") } } } extension AppDelegate: MessagingDelegate { func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { guard let token = fcmToken else { print("获取FCM Token失败") return } // 保存或更新Token saveFCMToken(token) } } extension AppDelegate : UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { let userInfo = notification.request.content.userInfo if let messageID = userInfo["gcm.message_id"] { print("Message ID: \(messageID)") } // 根据需求设置通知展示选项 completionHandler([.alert, .badge, .sound]) } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Messaging.messaging().apnsToken = deviceToken Messaging.messaging().setAPNSToken(deviceToken, type: .prod) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print("注册远程通知失败: \(error.localizedDescription)") } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo if let messageID = userInfo["gcm.message_id"] { print("Message ID from userNotificationCenter didReceive: \(messageID)") } completionHandler() } }
额外说明
didReceiveRegistrationToken方法会在Token首次生成、Token更新(比如App重装、设备重置等场景)时被调用,确保Token始终是最新的- 优化后的
saveFCMToken方法通过merge: true直接更新文档,避免了先查询再判断的冗余操作,同时保证即使文档不存在也能创建并写入Token - 注意处理用户未登录的情况,避免因
uid为nil导致保存失败
内容的提问来源于stack exchange,提问作者Swink
相关产品推荐
相关产品推荐

