macOS Big Sur 11.6下点击通知未触发userNotificationCenter问题
问题描述
当我通过「Product / Scheme / Edit Scheme… / Run / Arguments / Argument Passes On Launch」路径设置terminate启动参数运行应用时,macOS通知中心会弹出一条通知,随后应用终止。
#testapp application did finish launching #testapp terminate mode enabled #testapp terminating…
到这里为止表现都符合预期,没有问题。
当我点击这条通知时,应用会启动,但userNotificationCenter方法没有被触发:我在控制台应用中看不到#testapp notification triggered日志,仅能看到如下输出:
#testapp application did finish launching #testapp terminating…
这显然是不正常的,请问我该如何修复这个问题?
我目前怀疑这是Big Sur 11.6版本的系统bug。
该功能在Big Sur 11.4(M1芯片)和11.5(Intel芯片)版本上均可正常运行。
测试代码如下:
// // AppDelegate.swift // Test // // Created by Sun Knudsen on 2021-10-22. // import Cocoa import UserNotifications @main class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate { func showNotification(){ let content = UNMutableNotificationContent() content.body = "Hello" let request = UNNotificationRequest( identifier: UUID().uuidString, content: content, trigger: nil ) UNUserNotificationCenter.current().add(request) } func terminate() -> Void { DispatchQueue.main.async { NSApp.terminate(self) } } func applicationDidFinishLaunching(_ notification: Notification) { UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (allowed, error) in NSLog("#testapp application did finish launching") if CommandLine.arguments.indices.contains(1) && CommandLine.arguments[1] == "terminate" { NSLog("#testapp terminate mode enabled") self.showNotification() self.terminate() } else { DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { self.terminate() } } } } func applicationWillTerminate(_ aNotification: Notification) { NSLog("#testapp terminating…") } func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { NSLog("#testapp notification triggered") self.terminate() completionHandler() } }
修复方案
原因说明
该问题确实是macOS Big Sur 11.6的已知系统兼容问题:当应用发送通知后立刻终止,用户点击通知冷启动应用时,系统对UNUserNotificationCenter的代理回调派发会出现延迟,回调还没执行就被代码里的1秒自动终止逻辑中断,所以看不到对应日志。
具体修改点
- 给发送通知后的终止逻辑增加短暂延迟,确保通知已经被系统成功接收再退出应用,避免通知丢失
- 针对11.6版本延长非启动参数模式下的自动终止等待时长,给系统预留足够的回调派发时间
- 新增通知接收标记,收到通知回调后就不需要等超时,可以直接终止应用
修复后代码
// // AppDelegate.swift // Test // // Created by Sun Knudsen on 2021-10-22. // import Cocoa import UserNotifications @main class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCenterDelegate { // 标记是否已收到通知点击回调 private var hasReceivedNotification = false func showNotification(){ let content = UNMutableNotificationContent() content.body = "Hello" let request = UNNotificationRequest( identifier: UUID().uuidString, content: content, trigger: nil ) UNUserNotificationCenter.current().add(request) } func terminate() -> Void { DispatchQueue.main.async { NSApp.terminate(self) } } func applicationDidFinishLaunching(_ notification: Notification) { UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { [weak self] (allowed, error) in guard let self = self else { return } NSLog("#testapp application did finish launching") if CommandLine.arguments.indices.contains(1) && CommandLine.arguments[1] == "terminate" { NSLog("#testapp terminate mode enabled") self.showNotification() // 增加0.5秒延迟,确保通知被系统接收后再退出 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.terminate() } } else { // 11.6版本等待5秒,其他版本等待1秒 let waitTime: TimeInterval = ProcessInfo.processInfo.isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 11, minorVersion: 6, patchVersion: 0)) ? 5 : 1 DispatchQueue.main.asyncAfter(deadline: .now() + waitTime) { // 没有收到回调再执行终止 if !self.hasReceivedNotification { self.terminate() } } } } } func applicationWillTerminate(_ aNotification: Notification) { NSLog("#testapp terminating…") } func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { hasReceivedNotification = true NSLog("#testapp notification triggered") self.terminate() completionHandler() } }
内容的提问来源于stack exchange,提问作者sunknudsen
相关产品推荐
相关产品推荐

