macOS 26 SwiftUI 应用启动即退出(exit 0、无崩溃日志):MenuBarExtra 图标被系统隐藏


异常描述

macOS 26(Tahoe)上,一个 SwiftUI 菜单栏应用(MenuBarExtra + Window)启动即消失,控制台刷出大量错误:

1
2
3
4
5
Unable to get synchronousRemoteObjectProxy, error: Error Domain=NSCocoaErrorDomain
Code=4097 "connection to service named com.apple.linkd.autoShortcut" ...
Error registering app with intents framework: ...
Unable to re-register with Process Instance Registry, error: ...
Will NOT re-try to establish the connection

关键特征:

  • 直接运行二进制,进程约 0.3 秒退出,exit code 0
  • DiagnosticReports / Sentry 均无崩溃报告
  • 应用在启动时调用了 NSApp.setActivationPolicy(.accessory)(程序坞隐藏模式)
  • 菜单栏图标恰好被系统隐藏

异常分析

  1. 刷屏的 linkd.autoShortcut 错误是干扰项,每次启动都有,与退出无因果关系。
  2. exit 0 说明不是 crash,而是进程被优雅终止log stream 捕获的临终序列:
1
2
3
4
5
No windows open yet
→ _NSEnableAutomaticTermination
→ 创建 MenuBarExtra 状态项 scene
→ NSStatusItemChangeVisibilityAction(图标被系统隐藏)
→ terminate:
  1. 这是 macOS 26 的 SwiftUI 缺陷:MenuBarExtra 把「图标可见」绑进了 scene 生命周期。三个条件同时满足时,应用没有任何可见 UI 锚点,被 Automatic Termination 回收:
条件 说明
MenuBarExtra scene 图标被隐藏后该 scene 失效
.accessory 激活策略 无程序坞图标
菜单栏图标被系统隐藏 控制中心写入,应用不可控

验证方法:

1
2
defaults read <bundle-id> | grep NSStatusItem
# 出现 "NSStatusItem VisibleCC Item-0" = 0 即图标被系统隐藏

解决办法

只在菜单栏图标可见时才允许 .accessory,否则回退 .regular 保留程序坞图标。所有策略切换统一收口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// 菜单栏图标是否被系统隐藏(控制中心写入的可见性缓存)
static var menuBarItemHiddenBySystem: Bool {
let defaults = UserDefaults.standard
return defaults.dictionaryRepresentation().keys.contains { key in
key.hasPrefix("NSStatusItem Visible")
&& (defaults.object(forKey: key) as? NSNumber)?.boolValue == false
}
}

/// 仅当图标可见时才允许切到 accessory,否则回退 regular
static func applyAccessoryPreference(_ wantsAccessory: Bool) {
let policy: NSApplication.ActivationPolicy =
wantsAccessory && !menuBarItemHiddenBySystem ? .accessory : .regular
NSApp.setActivationPolicy(policy)
}

func applicationWillFinishLaunching(_ notification: Notification) {
if shouldLaunchAsAccessory {
Self.applyAccessoryPreference(true) // 替代直接 setActivationPolicy(.accessory)
}
}

系统在运行期间仍可能随时隐藏图标,补一个每秒重评的守护(图标恢复可见后会自动切回 accessory):

1
2
3
4
5
6
7
8
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
Task { @MainActor [weak self] in
guard let self else { return }
let wantsAccessory = self.shouldLaunchAsAccessory
|| NSApp.activationPolicy() == .accessory
Self.applyAccessoryPreference(wantsAccessory)
}
}

修复后验证(原必死组合 = accessory 开关开启 + 图标被隐藏):

组合 修复前 修复后
accessory + 图标隐藏 ❌ 0.3s 退出 ✅ 存活,图标恢复可见后自动切回 accessory
accessory + 图标可见
非 accessory