UNUserNotificationCenterDelegate didReceive在应用未运行时未触发本地通知响应
我之前也踩过这个一模一样的坑!问题核心在于应用冷启动时,UNUserNotificationCenterDelegate还没来得及完成注册,导致系统无法触发didReceive回调。下面是分场景的完整解决方案,覆盖iOS12及更早、iOS13+的情况:
一、先把代理设置时机提前
不管是哪种iOS版本,代理设置一定要在应用启动的最早阶段完成,不能等到页面加载后再设置:
- iOS12及更早(仅AppDelegate):在
application:didFinishLaunchingWithOptions:里直接设置代理func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // 优先设置通知中心代理 UNUserNotificationCenter.current().delegate = self // 其他初始化代码... return true } - iOS13+(含SceneDelegate):除了AppDelegate,还要在Scene的初始化方法里再设置一次代理
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { // 同样优先设置代理 UNUserNotificationCenter.current().delegate = self // 其他Scene初始化代码... }
二、处理冷启动时的通知参数
应用关闭后点击通知启动,通知信息会被包含在启动选项里,这时候要先提取参数,再统一处理跳转:
1. AppDelegate中提取冷启动通知
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { UNUserNotificationCenter.current().delegate = self // 检查启动参数里的通知响应 if let notificationResponse = launchOptions?[.notificationResponse] as? UNNotificationResponse { // 这里不要直接跳转,因为界面可能还没初始化完成 // 可以用通知中心转发,或者延迟执行 DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.handleNotificationJump(notificationResponse) } } return true }
2. SceneDelegate中提取冷启动通知
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { UNUserNotificationCenter.current().delegate = self // 检查Scene连接参数里的通知响应 if let notificationResponse = connectionOptions.notificationResponse { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.handleNotificationJump(notificationResponse) } } }
三、统一跳转逻辑
把跳转代码抽成公共方法,不管是冷启动、前台还是后台收到通知,都调用同一个方法,避免重复代码:
// 公共跳转方法 private func handleNotificationJump(_ response: UNNotificationResponse) { // 从通知的userInfo里获取你预先设置的跳转标识(比如目标VC的Storyboard ID) let userInfo = response.notification.request.content.userInfo guard let targetVCId = userInfo["targetVCIdentifier"] as? String else { return } // 获取当前根控制器,这里根据你的项目结构调整(比如是NavigationController还是TabBarController) guard let window = UIApplication.shared.windows.first, let rootNav = window.rootViewController as? UINavigationController else { return } // 实例化目标ViewController并跳转 let targetVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: targetVCId) rootNav.pushViewController(targetVC, animated: true) }
然后在UNUserNotificationCenterDelegate的回调里也调用这个方法:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { handleNotificationJump(response) completionHandler() // 一定要调用完成回调 }
四、关键注意事项
- 发送通知时,一定要在
userInfo里添加唯一的跳转标识(比如目标VC的ID、页面类型枚举值),否则无法判断要跳转到哪个页面; - 冷启动时不要立刻跳转,因为界面可能还没初始化完成,用
DispatchQueue.main.asyncAfter延迟一点执行会更稳妥; - 如果项目用了TabBarController,跳转前要先切换到对应的Tab,再push目标VC。
内容的提问来源于stack exchange,提问作者cauende
相关产品推荐
相关产品推荐

