SwiftUI应用中两种方式获取的FCM Token不一致问题排查
SwiftUI中FCM Token获取不一致的问题
我在SwiftUI应用里通过Messaging.messaging().fcmToken获取的Token,和MessagingDelegate回调里拿到的Token不一致,想知道原因和解决办法。
我的代码实现
1. App结构体中获取Token的方式
@main struct iOSApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate var body: some Scene { WindowGroup { let isLogin = UserDefaults.standard.bool(forKey: "isLogin") let isUser = UserDefaults.standard.bool(forKey: "isUser") let token = Messaging.messaging().fcmToken// 此处期望获取与回调一致的Token if(isLogin == false){ LoginView(myFCMToken: token ?? "fcmioserrordan") }else { if(isUser == true){ UsersView(myFCMToken: token ?? "fcmioserrordan") }else{ RestaurantView(myFCMToken: token ?? "fcmioserrordan") } } } } }
2. MessagingDelegate回调获取Token
extension AppDelegate: MessagingDelegate { func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { let deviceToken:[String: String] = ["token": fcmToken ?? ""] print("Device token: ", deviceToken) // 此Token可正常用于FCM通知测试 } }
完整AppDelegate代码
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, *) { // 针对iOS 10的显示通知(通过APNS发送) 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) } }
UNUserNotificationCenterDelegate扩展代码
@available(iOS 10, *) extension AppDelegate : UNUserNotificationCenterDelegate { // 接收iOS 10设备的显示通知 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { let userInfo = notification.request.content.userInfo if let messageID = userInfo[gcmMessageIDKey] { print("Message ID: \(messageID)") } print(userInfo) // 可修改为你偏好的展示选项 completionHandler([[.banner, .badge, .sound]]) } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {} func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {} func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo if let messageID = userInfo[gcmMessageIDKey] { print("Message ID from userNotificationCenter didReceive: \(messageID)") } print(userInfo) completionHandler() } }
问题原因
- 加载时机不匹配:App结构体的
body会在应用启动初期就执行,此时Firebase可能还没完成初始化,fcmToken返回的是缓存的旧Token或空值;而didReceiveRegistrationToken回调是在Firebase成功获取/刷新Token后才触发,拿到的是最新有效Token。 - Token自动刷新机制:FCM Token会定期自动刷新,或在应用重装、设备系统更新等场景下变更。
fcmToken属性可能缓存旧值,回调则会在Token更新时立即返回新值。 - APNS注册未完成:FCM依赖APNS设备Token生成自身Token,App启动时
application.registerForRemoteNotifications()流程还没走完,导致fcmToken无法获取最新有效Token,而回调会等APNS注册完成后才拿到正确的FCM Token。
解决方案
方案1:回调中同步Token到UserDefaults
在didReceiveRegistrationToken回调里把最新Token存入UserDefaults,后续从UserDefaults读取:
extension AppDelegate: MessagingDelegate { func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { guard let token = fcmToken else { return } UserDefaults.standard.set(token, forKey: "latestFCMToken") print("Device token: ", token) } }
修改App结构体中的Token获取逻辑:
let token = UserDefaults.standard.string(forKey: "latestFCMToken") ?? "fcmioserrordan"
方案2:用ObservableObject监听Token变化
创建全局Token管理类,通过@Published属性实时发布Token更新:
class FCMTokenManager: ObservableObject { @Published var currentToken: String? init() { // 初始化先读取缓存Token currentToken = UserDefaults.standard.string(forKey: "latestFCMToken") // 设置代理监听Token更新 Messaging.messaging().delegate = self } } extension FCMTokenManager: MessagingDelegate { func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { currentToken = fcmToken if let token = fcmToken { UserDefaults.standard.set(token, forKey: "latestFCMToken") } } }
在App结构体中注入管理器并使用:
@main struct iOSApp: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate @StateObject private var tokenManager = FCMTokenManager() var body: some Scene { WindowGroup { let isLogin = UserDefaults.standard.bool(forKey: "isLogin") let isUser = UserDefaults.standard.bool(forKey: "isUser") if(isLogin == false){ LoginView(myFCMToken: tokenManager.currentToken ?? "fcmioserrordan") }else { if(isUser == true){ UsersView(myFCMToken: tokenManager.currentToken ?? "fcmioserrordan") }else{ RestaurantView(myFCMToken: tokenManager.currentToken ?? "fcmioserrordan") } } } } }
内容的提问来源于stack exchange,提问作者dhaval123
相关产品推荐
相关产品推荐

