Swift实现设备间FCM推送通知及APNS Token错误排查
问题描述
在两部iPhone上部署了仅含按钮的简单应用,期望点击按钮向另一设备发送推送通知,已在Firebase控制台创建项目,但始终无法实现跨设备推送,报错如下:
APNS device token not set before retrieving FCM Token for Sender ID '230481463908'. Be sure to re-retrieve the FCM token once the APNS device token is set.
原ViewController代码:
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() } func sendPushNotification(to token: String, title: String, body: String) { // Set up the message content let message = ["to": token, "priority": "high", "notification": ["title": title, "body": body, "badge": 1, "sound": "default"]] as [String: Any] // Serialize the message to JSON guard let jsonData = try? JSONSerialization.data(withJSONObject: message, options: []) else { print("Failed to serialize message to JSON") return } // Create the request object guard let url = URL(string: "https://fcm.googleapis.com/fcm/send") else { print("Failed to create URL object") return } var request = URLRequest(url: url) request.httpMethod = "POST" request.setValue("application/json", forHTTPHeaderField: "Content-Type") request.setValue("key=<YOUR_SERVER_KEY>", forHTTPHeaderField: "Authorization") request.httpBody = jsonData // Send the request let session = URLSession.shared let task = session.dataTask(with: request) { data, response, error in if let error = error { print("Failed to send notification: \(error.localizedDescription)") return } print("Notification sent successfully") } task.resume() } @IBAction func sendNotification(_ sender: Any) { print("gfcyfc") sendPushNotification(to: "5277df78e6354334eb84d5644c349d34c83f102d9abfbe5843c7cac5af1f27e0", title: "Hello", body: "Hello How are your") } }
原AppDelegate代码:
import UIKit import FirebaseCore import FirebaseMessaging import UserNotifications @main class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate, UNUserNotificationCenterDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() Messaging.messaging().delegate = self UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { success, _ in guard success else{ return } print("Registration Successful") } application.registerForRemoteNotifications() return true } func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { messaging.token { token, _ in guard let token = token else { return } print("Tokennnnnnnnn\(token)") } } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // Convert device token to string format let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined() print("Device token: \(deviceTokenString)") Messaging.messaging().setAPNSToken(deviceToken, type: .unknown) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print("Failed to register for remote notifications: \(error.localizedDescription)") } func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Messaging.messaging().apnsToken = deviceToken } }
解决方案
1. 清理AppDelegate中的重复方法
AppDelegate中存在两个签名重复的didRegisterForRemoteNotificationsWithDeviceToken方法,导致APNS Token设置逻辑冲突。删除不带下划线的旧方法,保留标准实现并主动重新获取FCM Token:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { let deviceTokenString = deviceToken.map { String(format: "%02x", $0) }.joined() print("Device token: \(deviceTokenString)") // 根据当前环境选择.prod或.dev Messaging.messaging().setAPNSToken(deviceToken, type: .prod) // 主动获取最新FCM Token Messaging.messaging().token { token, error in if let error = error { print("Failed to fetch FCM token: \(error.localizedDescription)") } else if let token = token { print("FCM Token: \(token)") // 可选:将Token上传至服务器统一管理 } } } // 移除以下重复方法 // func application(application: UIApplication, // didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // Messaging.messaging().apnsToken = deviceToken // }
2. 简化FCM Token回调逻辑
优化didReceiveRegistrationToken回调,直接使用传入的Token参数:
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { guard let token = fcmToken else { return } print("FCM Token Updated: \(token)") }
3. 验证核心配置
- 替换服务器密钥:将
sendPushNotification方法中Authorization头的<YOUR_SERVER_KEY>替换为Firebase控制台「项目设置」→「云消息传递」中的服务器密钥 - 开启Xcode权限:确保项目「Signing & Capabilities」中已开启「Push Notifications」,并在「Background Modes」中勾选「Remote notifications」
- 确认APNS证书:Firebase控制台需上传对应环境(开发/生产)的APNS证书,与Xcode打包环境匹配
- 检查通知权限:确认用户已允许通知,
requestAuthorization的success返回true
4. 优化推送请求的错误排查
修改网络请求回调,打印响应内容以便定位问题:
let task = session.dataTask(with: request) { data, response, error in if let error = error { print("Failed to send notification: \(error.localizedDescription)") return } // 打印FCM响应内容,排查推送失败原因 if let data = data, let responseString = String(data: data, encoding: .utf8) { print("FCM Response: \(responseString)") } print("Notification request sent") }
5. 跨设备推送操作步骤
- 在设备A上运行应用,获取并记录其FCM Token
- 将设备A的FCM Token替换到设备B的
sendNotification方法的to参数中 - 运行设备B,点击按钮即可向设备A发送推送通知
内容的提问来源于stack exchange,提问作者Marick Sumanta
相关产品推荐
相关产品推荐

