iOS Firebase通知:APP被杀状态下点击通知跳转指定视图问题
嘿,这个问题我熟!当App处于被杀状态时,点击推送的启动逻辑和后台/前台完全不一样,得专门处理冷启动时的参数传递。我给你一步步拆解解决方法:
核心原因
当App被杀后点击推送启动,推送的参数不会走didReceiveRemoteNotification方法,而是会被包含在启动参数里(launchOptions或者SceneDelegate的connectionOptions),所以我们要在App初始化的阶段就提取并处理这些参数。
解决步骤(分两种架构情况)
1. 传统AppDelegate架构(iOS 12及以下)
在didFinishLaunchingWithOptions里,先完成窗口和根视图的初始化,再检查启动参数里的推送信息,最后解析跳转逻辑:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { FirebaseApp.configure() // 先初始化窗口和根视图控制器(确保这部分代码在处理推送之前) let window = UIWindow(frame: UIScreen.main.bounds) self.window = window let rootNav = UINavigationController(rootViewController: HomeViewController()) window.rootViewController = rootNav window.makeKeyAndVisible() // 处理冷启动时的推送通知 if let pushPayload = launchOptions?[.remoteNotification] as? [String: Any] { handlePushNavigation(with: pushPayload) } return true } // 封装通用的跳转处理方法 private func handlePushNavigation(with payload: [String: Any]) { // 假设推送payload里有自定义字段`targetScreen`,标记要跳转的页面 guard let targetScreen = payload["targetScreen"] as? String else { print("未找到跳转标识") return } // 确保根视图是导航控制器(如果是TabBar需调整逻辑) guard let rootNav = window?.rootViewController as? UINavigationController else { print("根视图不是导航控制器,无法跳转") return } // 根据标识跳转对应页面 switch targetScreen { case "orderDetail": // 从payload提取额外参数,比如订单ID let orderId = payload["orderId"] as? String ?? "" let detailVC = OrderDetailViewController(orderId: orderId) rootNav.pushViewController(detailVC, animated: true) case "messageCenter": let msgVC = MessageCenterViewController() rootNav.pushViewController(msgVC, animated: true) default: // 默认不跳转或返回首页 break } }
2. SceneDelegate架构(iOS 13及以上)
iOS 13后启动逻辑移到了SceneDelegate,需要在willConnectTo方法里处理推送参数:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } // 初始化窗口和根视图 let window = UIWindow(windowScene: windowScene) self.window = window let rootNav = UINavigationController(rootViewController: HomeViewController()) window.rootViewController = rootNav window.makeKeyAndVisible() // 处理冷启动的推送通知 if let notificationResponse = connectionOptions.notificationResponse { let pushPayload = notificationResponse.notification.request.content.userInfo handlePushNavigation(with: pushPayload) } } // 复用通用跳转方法 private func handlePushNavigation(with payload: [String: Any]) { guard let targetScreen = payload["targetScreen"] as? String else { return } guard let rootNav = window?.rootViewController as? UINavigationController else { return } switch targetScreen { case "orderDetail": let orderId = payload["orderId"] as? String ?? "" let detailVC = OrderDetailViewController(orderId: orderId) rootNav.pushViewController(detailVC, animated: true) case "messageCenter": let msgVC = MessageCenterViewController() rootNav.pushViewController(msgVC, animated: true) default: break } }
关键注意事项
- 后端配合:推送payload必须包含自定义跳转标识(比如
targetScreen),要让后端把这个字段放在aps字典外面,示例payload:{ "aps": { "alert": { "title": "您的订单已发货", "body": "点击查看详情" }, "sound": "default" }, "targetScreen": "orderDetail", "orderId": "123456" } - 跳转时机:一定要先完成窗口和根视图的初始化,再处理跳转,否则会找不到导航控制器导致失败。
- 测试方式:必须把App完全杀掉(从后台向上划掉),再点击推送测试,不能在后台或前台测试冷启动场景。
内容的提问来源于stack exchange,提问作者desila




