React Native 0.77.0 iOS端FCM Token获取报错求助
React Native 0.77.0 iOS推送通知FCM Token获取失败排查
问题现象
在React Native 0.77.0环境下配置iOS推送通知时,调用getToken获取FCM Token抛出以下错误:
App.js:296 Error in token retrieval: NativeFirebaseError: [messaging/unknown] The operation couldn’t be completed. No APNS token specified before fetching FCM Token
用户提供的代码如下:
AppDelegate.swift代码
import UIKit import Firebase import UserNotifications import RNCPushNotificationIOS import React import React_RCTAppDelegate import ReactAppDependencyProvider @main class AppDelegate: RCTAppDelegate, UNUserNotificationCenterDelegate, MessagingDelegate { override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // Initialize Firebase FirebaseApp.configure(); Messaging.messaging().delegate = self // Set the UNUserNotificationCenter delegate to self UNUserNotificationCenter.current().delegate = self // Register for remote notifications application.registerForRemoteNotifications() // Request notification permissions UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in if let error = error { print("Error requesting notification permissions: \(error)") } print("Notification permission granted: \(granted)") } // You can add your custom initial props in the dictionary below. // They will be passed down to the ViewController used by React Native. self.initialProps = [:] self.moduleName = "*****" self.dependencyProvider = RCTAppDependencyProvider() return super.application(application, didFinishLaunchingWithOptions: launchOptions) } // // Handle foreground notifications: show alert, sound, and badge even when app is active. // func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { // completionHandler([.badge, .sound]) // } // // Optionally, handle notification responses if needed. // func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { // // Process the notification response (for example, navigate to a specific screen) // completionHandler() // } override func sourceURL(for bridge: RCTBridge) -> URL? { self.bundleURL() } override func bundleURL() -> URL? { #if DEBUG RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") #else Bundle.main.url(forResource: "main", withExtension: "jsbundle") #endif } override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Messaging.messaging().apnsToken = deviceToken } }
App.js中checkAndGetFcmToken函数代码
async function checkAndGetFcmToken() { try { if (Platform.OS === 'ios') { await messaging().registerDeviceForRemoteMessages(); } if (Platform.OS === 'android') { const granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS ); if (granted !== PermissionsAndroid.RESULTS.GRANTED) { console.log('Notification permission not granted'); return; } } const authStatus = await messaging().requestPermission(); const enabled = authStatus === messaging.AuthorizationStatus.AUTHORIZED || authStatus === messaging.AuthorizationStatus.PROVISIONAL; if (enabled) { const token = await messaging().getToken(); console.log('FCM token:', token); if (token) { await AsyncStorage.setItem('fcmToken', token); } else { console.warn('FCM token is still null.'); } } else { console.log('Notification permission not granted'); } } catch (error) { console.error('Error in token retrieval:', error); } }
问题根源
- 时序逻辑错误:Native层先调用
application.registerForRemoteNotifications(),再请求权限,导致可能在用户未授权的情况下就发起APNS注册,流程顺序颠倒。 - APNS Token未及时同步:
getToken调用时机过早,此时系统还未完成APNS Token的回调,Messaging.messaging().apnsToken尚未赋值,FCM无法生成对应Token。 - 重复注册操作:iOS平台下,Native层已经调用
application.registerForRemoteNotifications(),JS层又调用messaging().registerDeviceForRemoteMessages(),重复操作导致时序混乱。
修复方案
1. 调整Native层流程顺序
先请求通知权限,用户授权后再注册远程通知,确保APNS注册流程的合法性。同时在APNS Token获取后主动触发FCM Token刷新,并监听FCM Token的更新事件。
2. 优化JS层调用逻辑
移除iOS平台下的重复注册代码,改用监听Token刷新事件的方式获取FCM Token,避免因时序问题导致的获取失败。
修改后的代码
AppDelegate.swift
import UIKit import Firebase import UserNotifications import RNCPushNotificationIOS import React import React_RCTAppDelegate import ReactAppDependencyProvider @main class AppDelegate: RCTAppDelegate, UNUserNotificationCenterDelegate, MessagingDelegate { override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // Initialize Firebase FirebaseApp.configure(); Messaging.messaging().delegate = self // Set the UNUserNotificationCenter delegate to self UNUserNotificationCenter.current().delegate = self // 先请求权限,授权后再注册远程通知 UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in if let error = error { print("Error requesting notification permissions: \(error)") } print("Notification permission granted: \(granted)") if granted { DispatchQueue.main.async { application.registerForRemoteNotifications() } } } self.initialProps = [:] self.moduleName = "*****" self.dependencyProvider = RCTAppDependencyProvider() return super.application(application, didFinishLaunchingWithOptions: launchOptions) } // 监听FCM Token更新事件 func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { print("FCM Token updated: \(fcmToken ?? "nil")") } override func sourceURL(for bridge: RCTBridge) -> URL? { self.bundleURL() } override func bundleURL() -> URL? { #if DEBUG RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") #else Bundle.main.url(forResource: "main", withExtension: "jsbundle") #endif } override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Messaging.messaging().apnsToken = deviceToken // 设置APNS Token后主动刷新FCM Token Messaging.messaging().token(completion: { token, error in if let error = error { print("Failed to refresh FCM token: \(error)") } else if let token = token { print("FCM token refreshed: \(token)") } }) } // 处理远程通知注册失败的情况 override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print("Failed to register for remote notifications: \(error)") } }
App.js checkAndGetFcmToken函数
async function checkAndGetFcmToken() { try { if (Platform.OS === 'android') { const granted = await PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS ); if (granted !== PermissionsAndroid.RESULTS.GRANTED) { console.log('Notification permission not granted'); return; } } const authStatus = await messaging().requestPermission(); const enabled = authStatus === messaging.AuthorizationStatus.AUTHORIZED || authStatus === messaging.AuthorizationStatus.PROVISIONAL; if (enabled) { // 监听FCM Token刷新事件 const unsubscribe = messaging().onTokenRefresh(async (token) => { console.log('FCM token refreshed:', token); await AsyncStorage.setItem('fcmToken', token); unsubscribe(); }); // 尝试获取当前Token,失败则等待刷新事件 try { const token = await messaging().getToken(); if (token) { console.log('FCM token:', token); await AsyncStorage.setItem('fcmToken', token); unsubscribe(); } } catch (getTokenError) { console.log('Waiting for FCM token refresh...'); } } else { console.log('Notification permission not granted'); } } catch (error) { console.error('Error in token retrieval:', error); } }
内容的提问来源于stack exchange,提问作者Raj M
相关产品推荐
相关产品推荐

