macOS菜单栏App启动崩溃:SwiftUI/AppWindowsController.swift:89致命错误求助
解决macOS菜单栏应用
SwiftUI/AppWindowsController.swift:89崩溃问题 问题描述
运行macOS菜单栏应用时触发致命错误:SwiftUI/AppWindowsController.swift:89: Fatal error
开发环境:Xcode 15.2、Swift 5.9.2、最低部署版本macOS 14.2,无论将Info.plist中LSUIElement(对应Application is agent (UI Element))设为YES或NO,应用都会崩溃,怀疑和AppStorage有关。
崩溃原因
- 重复绑定
AppStorage键:在GiveMeTwentyApp和ContentView中同时声明了@AppStorage(SettingsKeys.showAppInMenuBar),当该值通过ContentView的Toggle修改时,会同时触发App层MenuBarExtra的插入/移除操作,导致Scene结构动态变化,窗口管理逻辑无法处理引发崩溃。 - LSUIElement与WindowGroup冲突:当应用以代理模式(LSUIElement=YES)运行时,普通
WindowGroup的显示逻辑与菜单栏应用的窗口管理存在冲突,进一步加剧崩溃概率。
解决方案
1. 统一状态管理,避免重复绑定
将所有AppStorage状态提升到App结构体中,通过Binding传递给ContentView,避免重复声明同一个存储键。
2. 条件控制WindowGroup显示
当LSUIElement设为YES时,隐藏普通WindowGroup,避免窗口管理冲突。
修复后的完整代码
App结构体代码
import SwiftUI @main struct GiveMeTwentyApp: App { @AppStorage(SettingsKeys.showAppInMenuBar) var showAppInMenuBar: Bool = true @AppStorage(SettingsKeys.reminderFrequency) var reminderFrequency: Int = 1 @AppStorage(SettingsKeys.notificationsEnabled) var notificationsEnabled: Bool = true @AppStorage(SettingsKeys.runWhenComputerStarts) var runWhenComputerStarts: Bool = true @AppStorage(SettingsKeys.popUpMenuMessage) var popUpMenuMessage: String = "Give Me Twenty!" // 获取LSUIElement配置值 private var isAgentApp: Bool { Bundle.main.object(forInfoDictionaryKey: "LSUIElement") as? Bool ?? false } var body: some Scene { #if os(macOS) MenuBarExtra(isInserted: $showAppInMenuBar) { ContentView( reminderFrequency: $reminderFrequency, notificationsEnabled: $notificationsEnabled, runWhenComputerStarts: $runWhenComputerStarts, showAppInMenuBar: $showAppInMenuBar, popUpMenuMessage: $popUpMenuMessage ) .navigationTitle("GiveMeTwenty") } label: { Label("GiveMeTwenty", systemImage: "star") } .menuBarExtraStyle(.window) #endif // 仅在非代理模式或隐藏菜单栏时显示普通窗口 if !isAgentApp || !showAppInMenuBar { WindowGroup { ContentView( reminderFrequency: $reminderFrequency, notificationsEnabled: $notificationsEnabled, runWhenComputerStarts: $runWhenComputerStarts, showAppInMenuBar: $showAppInMenuBar, popUpMenuMessage: $popUpMenuMessage ) .navigationTitle("GiveMeTwenty") } } } }
ContentView代码
import SwiftUI struct ContentView: View { @Binding var reminderFrequency: Int @Binding var notificationsEnabled: Bool @Binding var runWhenComputerStarts: Bool @Binding var showAppInMenuBar: Bool @Binding var popUpMenuMessage: String private let reminderFrequencyOptions = [1, 2, 3, 4, 5, 6, 7, 8] var body: some View { VStack(alignment: .leading) { HStack { Picker("Every", selection: $reminderFrequency) { ForEach(reminderFrequencyOptions, id: \.self) { Text($0.description) } } .frame(width: 80) .clipped() Text("hours take a break.") } .padding() Toggle(isOn: $notificationsEnabled) { Text("Enable notifications") } .toggleStyle(.checkbox) .padding() Toggle(isOn: $runWhenComputerStarts) { Text("Run when computer starts") } .toggleStyle(.checkbox) .padding() Toggle(isOn: $showAppInMenuBar) { Text("Show app in menu bar") } .toggleStyle(.checkbox) .padding() } .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .topLeading) .padding() } }
SettingsKeys代码(保持不变)
import Foundation enum SettingsKeys { static let reminderFrequency = "reminderFrequency" static let runWhenComputerStarts = "runWhenComputerStarts" static let showAppInMenuBar = "showAppInMenuBar" static let popUpMenuMessage = "popUpMenuMessage" static let notificationsEnabled = "notificationsEnabled" }
额外注意事项
- 测试前建议清除应用的UserDefaults数据(可通过删除容器或重置模拟器解决),避免旧的存储值干扰。
- 若仍有崩溃,可尝试移除
menuBarExtraStyle(.window),改用默认的菜单样式测试,排查窗口样式是否引发问题。
内容的提问来源于stack exchange,提问作者N.K.
相关产品推荐
相关产品推荐

