You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 08:15:04