Android向iOS发送FCM推送失败,求代码示例及解决方案
问题:Android无法向iOS发送FCM推送通知
- Android到Android:推送正常
- iOS到iOS:推送正常
- iOS到Android:推送正常
- Android到iOS:推送失败
用户怀疑问题出在Kotlin代码中,以下是相关代码:
Kotlin 相关代码
NotificationService 类
class NotificationService: FirebaseMessagingService() { ... fun sendNotification(notification: PushNotification) = CoroutineScope(Dispatchers.IO).launch { try { val response = RetrofitInstance.api.postNotification(notification) if (response.isSuccessful) { println("RESPONSE : ${Gson().toJson(response)}") println("OK") }else { println("RESPONSE ERROR: ${response.errorBody().toString()}") } }catch (e: Exception) { println("ERROR: ${e.toString()}") } } fun send(title: String, message: String, token: String) { PushNotification(NotificationData(title, message), token).also { sendNotification(it) } } }
调用方式:NotificationService().send("My title", "My message", "A_GIVEN_TOKEN")
RetrofitInstance 类
class RetrofitInstance { companion object { private val retrofit by lazy { Retrofit.Builder() .baseUrl(BASE_URL) //BASE_URL = "https://fcm.googleapis.com" .addConverterFactory(GsonConverterFactory.create()) .build() } val api by lazy { retrofit.create(NotificationAPI::class.java) } } }
NotificationAPI 接口
interface NotificationAPI { @Headers("Authorization: key=$SERVER_KEY", "Content-Type:$CONTENT_TYPE") //CONTENT_TYPE = "application/json" @POST("fcm/send") suspend fun postNotification( @Body notification: PushNotification ): Response<ResponseBody> }
PushNotification 数据类
data class PushNotification ( val data: NotificationData, val to: String)
NotificationData 数据类
data class NotificationData ( val title: String, val message: String )
iOS AppDelegate 代码
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate { ... func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { //FirebaseApp.configure() Messaging.messaging().delegate = self if #available(iOS 10.0, *) { // For iOS 10 display notification (sent via APNS) UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization(options: authOptions,completionHandler: {_, _ in }) // For iOS 10 data message (sent via FCM } else { let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil) application.registerUserNotificationSettings(settings) } application.registerForRemoteNotifications() return true } func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { guard let fcmToken = fcmToken else {return} let dataDict:[String: String] = ["token": fcmToken] NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict) // TODO: If necessary send token to application server. // Note: This callback is fired at each app startup and whenever a new token is generated. } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) { //DOESEN'T PASS HERE guard let aps = userInfo["aps"] as? [String: Any] else {return} guard let alert = aps["alert"] as? [String: String] else {return} guard let title = alert["title"] else {return} guard let body = alert["body"] else {return} let systemSoundID: SystemSoundID = 1007 AudioServicesPlaySystemSound(systemSoundID) print("TITLE : ", title) print("BODY : ", body) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { print("ERROOOOR: ", error) } func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { //PASS HERE let systemSoundID: SystemSoundID = 1007 // to play sound AudioServicesPlaySystemSound(systemSoundID) print(notification.request.content.userInfo) completionHandler([.alert, .badge, .sound]) } }
解决方案
问题核心:iOS接收FCM推送时,若要显示系统通知栏提醒,需要请求体包含notification字段;仅传data字段时,iOS后台/杀死状态下不会触发通知栏提醒,且前台需要手动解析数据。Android则可直接处理data字段。
1. 修改Kotlin端推送数据结构
新增支持notification字段的模型,同时保留data字段兼容Android:
更新PushNotification及相关数据类
data class PushNotification( val data: NotificationData? = null, val notification: NotificationContent? = null, val to: String ) // 用于iOS系统通知栏显示的内容结构 data class NotificationContent( val title: String, val body: String, val sound: String = "default" // 指定通知音效,可选 ) // 原NotificationData保留,用于Android端数据解析 data class NotificationData( val title: String, val message: String )
修改NotificationService的send方法
同时传入data和notification字段,确保两端都能正常处理:
fun send(title: String, message: String, token: String) { PushNotification( data = NotificationData(title, message), notification = NotificationContent(title, message), to = token ).also { sendNotification(it) } }
2. 修复iOS端回调处理
补充后台数据接收逻辑,统一解析data和notification字段:
更新AppDelegate代码
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate, MessagingDelegate { ... func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // 必须启用Firebase配置,否则FCM无法工作 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 userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { let systemSoundID: SystemSoundID = 1007 AudioServicesPlaySystemSound(systemSoundID) let userInfo = notification.request.content.userInfo // 解析data字段 if let data = userInfo["data"] as? [String: String] { let title = data["title"] ?? "" let message = data["message"] ?? "" print("前台Data消息:\(title) - \(message)") } // 解析notification字段 if let aps = userInfo["aps"] as? [String: Any], let alert = aps["alert"] as? [String: String] { let title = alert["title"] ?? "" let body = alert["body"] ?? "" print("前台Notification消息:\(title) - \(body)") } completionHandler([.alert, .badge, .sound]) } // 点击通知时触发(后台/杀死状态) func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo // 解析逻辑同前台 if let data = userInfo["data"] as? [String: String] { let title = data["title"] ?? "" let message = data["message"] ?? "" print("点击通知收到Data消息:\(title) - \(message)") } if let aps = userInfo["aps"] as? [String: Any], let alert = aps["alert"] as? [String: String] { let title = alert["title"] ?? "" let body = alert["body"] ?? "" print("点击通知收到Notification消息:\(title) - \(body)") } completionHandler() } // 后台接收纯数据消息(无通知栏提醒) func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { if let data = userInfo["data"] as? [String: String] { let title = data["title"] ?? "" let message = data["message"] ?? "" print("后台Data消息:\(title) - \(message)") } completionHandler(.newData) } // 其他原有方法保留... }
3. 额外检查项
- 确认iOS项目已启用
FirebaseApp.configure() - 验证iOS设备的FCM令牌是最新且有效的
- 检查Firebase控制台中iOS应用的APNs证书是否正确(区分开发/生产环境)
内容的提问来源于stack exchange,提问作者Mr the cat
相关产品推荐
相关产品推荐

