FCM推送通知无法接收但OneSignal正常,求排查方案及代码审查
首先,咱们先梳理下可能遗漏的FCM配置点——毕竟OneSignal能正常工作,说明你的APNS基础配置是没问题的,问题大概率出在FCM专属的配置或者代码细节上:
一、排查FCM专属配置遗漏点
- Firebase控制台APNs证书验证:虽然OneSignal验证过证书,但要确认你在Firebase控制台的iOS应用中上传的APNs证书是否和当前环境匹配(开发环境用开发证书,Ad-hoc/生产用生产证书),并且证书的bundle ID和Xcode项目的bundle ID完全一致。另外,检查
GoogleService-Info.plist文件是否正确添加到Xcode项目中,且文件中的BUNDLE_ID、GOOGLE_APP_ID等字段和Firebase控制台的配置匹配。 - 推送消息类型与环境匹配:FCM推送分「通知消息」和「数据消息」,iOS后台状态下只有通知消息会由APNS直接展示,数据消息需要App处于前台或后台唤醒才能处理。另外,发送推送时要选择正确的环境:开发测试选「开发」,Ad-hoc/正式发布选「生产」,环境不匹配会导致推送无法送达。
- FCM Token与APNs Token关联:FCM需要关联APNs的device token才能向iOS设备推送,这一步如果没做好,就会直接导致推送失败——这也是你代码里的核心问题之一,咱们后面细说。
二、代码审查与修复建议
我仔细看了你的AppDelegate代码,发现几个关键问题:
1. Firebase初始化时机错误
你在AppDelegate的init()方法中调用FirebaseApp.configure(),这个时机太早了,系统的很多服务还没完成初始化,可能导致Firebase配置不完整。建议把这行代码移到application(_:didFinishLaunchingWithOptions:)方法的最开头:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() // 移到这里 // ...其他代码 }
同时删除init()方法中的FirebaseApp.configure()调用。
2. APNs Token关联方法签名错误
你的didRegisterForRemoteNotificationsWithDeviceToken方法签名不符合Swift 3+的规范,导致这个方法不会被系统调用,FCM也就无法获取到APNs的device token——这是推送失败的核心原因!
错误的签名:
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Messaging.messaging().apnsToken = deviceToken print("sdfksjkjfksajkdnslfs") }
修正后的正确签名(注意参数前的下划线):
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Messaging.messaging().apnsToken = deviceToken print("APNs token registered: \(deviceToken)") }
3. PushRegistry对象被提前释放
你在didFinishLaunchingWithOptions中创建的PKPushRegistry是局部变量,方法执行完后会被系统释放,导致VoIP推送的delegate无法生效。虽然这和普通FCM推送无关,但还是建议把它改成AppDelegate的属性:
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate, CXProviderDelegate, PKPushRegistryDelegate { private var voIPRegistry: PKPushRegistry? // 添加属性 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // ...其他代码 // PushKit voIPRegistry = PKPushRegistry(queue: nil) voIPRegistry?.delegate = self voIPRegistry?.desiredPushTypes = [PKPushType.voIP] // ...其他代码 } }
4. 通知授权后的注册优化
在iOS 10+的代码中,建议在用户授权通知后再调用application.registerForRemoteNotifications(),这样更符合系统逻辑,避免用户拒绝授权后仍注册推送:
if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: { granted, error in if granted { DispatchQueue.main.async { application.registerForRemoteNotifications() } } }) } else { let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil) application.registerUserNotificationSettings(settings) application.registerForRemoteNotifications() }
同时删除后面单独的application.registerForRemoteNotifications()调用,避免重复注册。
三、额外测试建议
- 重新获取FCM Token:修改代码后,重启App,查看控制台输出的
FCM token是否正常生成,并且确保这个token已经上传到你的服务器(如果需要自己发送推送的话)。 - 用Firebase控制台发送测试推送:选择正确的环境(开发/生产),输入获取到的FCM Token,发送通知消息,观察App是否能接收。
- 检查Xcode的Capabilities:确认「Push Notifications」和「Background Modes」(如果需要后台推送处理)已经开启,Background Modes中勾选「Remote notifications」。
内容的提问来源于stack exchange,提问作者Ryoma Kishimoto

