iOS FCM推送问题:APP完全终止时无法捕获自定义数据求助
FCM推送:APP完全终止时点击通知无法获取自定义数据的解决方案
问题核心
APP完全终止(进程被杀)时,点击FCM通知启动APP,UNUserNotificationCenterDelegate或Firebase代理方法未触发,无法读取自定义跳转数据;APP前台/后台运行时点击通知可正常处理。
后端PHP代码分析(你的实现)
<?php function sendFCMPush($token, $title, $body, $customData) { $url = 'https://fcm.googleapis.com/fcm/send'; $headers = [ 'Authorization: key=YOUR_SERVER_KEY', 'Content-Type: application/json' ]; $payload = [ 'to' => $token, 'notification' => [ 'title' => $title, 'body' => $body, 'sound' => 'default' ], 'data' => $customData // 自定义数据:如['type' => 'order', 'order_id' => '1001'] ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); $response = curl_exec($ch); curl_close($ch); return $response; } ?>
你的payload结构没问题,data字段会被系统传递到APP,但APP终止时,数据不会直接触发代理方法,而是被封装在启动参数中。
iOS Swift代码修复方案
1. 在didFinishLaunchingWithOptions中处理启动参数
APP终止时点击通知启动,自定义数据会被包含在launchOptions的.remoteNotification键下,必须在这里先处理:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 必须第一时间初始化Firebase并设置代理 FirebaseApp.configure() Messaging.messaging().delegate = self UNUserNotificationCenter.current().delegate = self // 处理终止状态下点击通知启动的场景 if let remoteNotificationData = launchOptions?[.remoteNotification] as? [AnyHashable: Any] { handleCustomNavigation(remoteNotificationData) } // 请求通知权限 let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization( options: authOptions, completionHandler: { _, _ in }) application.registerForRemoteNotifications() return true }
2. 确保代理方法覆盖所有场景
保留原有代理方法,同时统一处理逻辑:
// 前台收到通知时触发 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { let userInfo = notification.request.content.userInfo handleCustomNavigation(userInfo) completionHandler([.banner, .sound]) } // 后台/终止状态点击通知时触发(若代理设置及时) func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo handleCustomNavigation(userInfo) completionHandler() } // 接收纯数据消息(无notification字段的推送) func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) { handleCustomNavigation(remoteMessage.appData) } // 统一处理跳转逻辑 private func handleCustomNavigation(_ userInfo: [AnyHashable: Any]) { guard let type = userInfo["type"] as? String, let targetId = userInfo["order_id"] ?? userInfo["product_id"] as? String else { return } // 确保在主线程处理UI跳转 DispatchQueue.main.async { guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, let rootVC = windowScene.windows.first?.rootViewController else { return } // 根据type跳转到对应页面 if type == "order" { let orderVC = OrderDetailViewController(orderId: targetId) rootVC.show(orderVC, sender: nil) } else if type == "product" { let productVC = ProductDetailViewController(productId: targetId) rootVC.show(productVC, sender: nil) } } }
关键注意事项
- 代理设置时机:必须在
didFinishLaunchingWithOptions的最开始设置UNUserNotificationCenter和Messaging的代理,否则系统可能无法找到代理,导致方法不触发。 - payload验证:确保
data字段的键值对是可序列化的JSON类型(字符串、数字、布尔值),避免嵌套复杂对象。 - 测试方式:测试时必须完全终止APP(Xcode停止运行或多任务栏滑掉),再发送推送点击启动,后台状态的测试不能复现终止场景的问题。
- iOS版本兼容:iOS 10+必须使用
UNUserNotificationCenterDelegate,旧的application:didReceiveRemoteNotification:方法已被废弃。
内容的提问来源于stack exchange,提问作者Kodr.F
相关产品推荐
相关产品推荐

