You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

App启动时Firestore无法保存FCM Token的问题咨询

解决FCM Token首次启动无法保存到Firestore的问题

问题根源

首次启动App时,FCM Token的生成是异步操作,在application(_:didFinishLaunchingWithOptions:)中直接读取Messaging.messaging().fcmToken大概率会拿到nil,自然无法触发保存逻辑。而重启App后,Token已经被本地缓存,此时能读取到有效值,所以保存成功。另外你虽然实现了MessagingDelegate,但didReceiveRegistrationToken方法是空的,这个方法才是FCM Token生成或更新时的官方回调入口。

修复方案

  1. 删除didFinishLaunchingWithOptions中直接读取fcmToken并保存的代码段
  2. 在MessagingDelegate的didReceiveRegistrationToken方法中处理Token的保存逻辑,确保Token生成后立即触发保存
  3. 优化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 19:05:07