非AppDelegate中请求远程通知后,通知回调未触发的解决方法
需求与当前实现
需求:在OnboardingViewController中,用户点击「允许通知」按钮后发起推送通知授权请求。
当前OnboardingViewController实现代码:
class OnboardingViewController: UIViewController, UNUserNotificationCenterDelegate { func registerForRemoteNotification() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in if error == nil && granted { print("Brugeren har accepteret notifikationer") DispatchQueue.main.async { UNUserNotificationCenter.current().delegate = self UIApplication.shared.registerForRemoteNotifications() } } } } }
遇到的问题
采用上述实现时,AppDelegate中的以下通知响应方法无法被调用:
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { print("Test 1") completionHandler() }
只有将授权请求代码放到AppDelegate的didFinishLaunchingWithOptions方法中,该didReceive方法才会正常执行。
问题原因
核心问题是**UNUserNotificationCenter的代理被设置为OnboardingViewController实例,但当引导页完成使命被销毁(比如用户跳转至主页面),该代理对象会被释放,导致后续通知事件无法传递到AppDelegate**。
你在授权成功后将代理指向了当前的OnboardingViewController,而AppDelegate虽然实现了代理方法,但从未被设置为通知中心的代理,因此引导页销毁后,通知中心没有有效代理处理事件,自然触发不了AppDelegate中的方法。
解决方案
方案一:将AppDelegate设置为全局代理
修改OnboardingViewController中的授权代码,把通知中心的代理指向AppDelegate实例,而非当前控制器:
func registerForRemoteNotification() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in if error == nil && granted { print("Brugeren har accepteret notifikationer") DispatchQueue.main.async { // 获取AppDelegate实例并设置为通知中心代理 let appDelegate = UIApplication.shared.delegate as! AppDelegate UNUserNotificationCenter.current().delegate = appDelegate UIApplication.shared.registerForRemoteNotifications() } } } }
同时确保AppDelegate已遵守UNUserNotificationCenterDelegate协议:
@main class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { // ... 其他原有代码 }
方案二:创建独立的通知管理单例(推荐)
为避免页面控制器与AppDelegate耦合,更优雅的方式是创建全局单例统一管理通知逻辑:
class NotificationManager: NSObject, UNUserNotificationCenterDelegate { static let shared = NotificationManager() private override init() { super.init() // 初始化时就将自身设为通知中心代理 UNUserNotificationCenter.current().delegate = self } func requestNotificationAuthorization() { let center = UNUserNotificationCenter.current() center.requestAuthorization(options: [.sound, .alert, .badge]) { (granted, error) in if error == nil && granted { print("Brugeren har accepteret notifikationer") DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } } } } // 实现通知响应方法 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { print("Test 1") completionHandler() } // 可选:处理前台收到通知时的展示逻辑 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.sound, .alert, .badge]) } }
之后在OnboardingViewController中直接调用单例方法即可:
func registerForRemoteNotification() { NotificationManager.shared.requestNotificationAuthorization() }
这种方式将通知逻辑集中管理,不受页面生命周期影响,后续扩展通知功能也更便捷。
内容的提问来源于stack exchange,提问作者M. Holm

