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

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. 跨设备推送操作步骤

  1. 在设备A上运行应用,获取并记录其FCM Token
  2. 将设备A的FCM Token替换到设备B的sendNotification方法的to参数中
  3. 运行设备B,点击按钮即可向设备A发送推送通知

内容的提问来源于stack exchange,提问作者Marick Sumanta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 20:29:55