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

macOS SwiftUI中ViewModel先于AppDelegate执行的解决办法咨询

macOS SwiftUI中ViewModel先于AppDelegate执行的问题解决方案

执行顺序是否正常?

这种断点触发顺序(2→3→1)是正常现象。原因在于:

  • @NSApplicationDelegateAdaptor只是将AppDelegate接入SwiftUI的App生命周期,但AppDelegate的applicationDidFinishLaunching是在应用完成整个启动流程后才触发的系统回调。
  • SwiftUI的@StateObject会在App结构体初始化阶段就创建实例,这个时机早于applicationDidFinishLaunching的执行,因此会出现ViewModel先初始化、后执行UserDefaults注册的情况。

解决方案

方案1:延迟ViewModel初始化(适配遗留AppDelegate场景)

给AppDelegate添加启动完成的标志位,在applicationDidFinishLaunching中标记完成,再在SwiftUI App的生命周期回调中触发ViewModel的初始化:

// 修改AppDelegate.swift
final class AppDelegate: NSObject, NSApplicationDelegate {
    @Published var isLaunchFinished = false

    func applicationDidFinishLaunching(_ notification: Notification) {
        UserDefaults.shared.register(defaults: [
            "userDefaultItem1" : false,
            "userDefaultItem2" : 0
        ])
        isLaunchFinished = true
    }
}

// 修改AppName.swift
@main
struct AppName: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self)
    private var appDelegate
    
    @StateObject private var vm: ViewModel? = nil

    var body: some Scene {
        WindowGroup {
            if let vm = vm {
                ContentView()
                    .environmentObject(vm)
            } else {
                ProgressView("启动中...")
            }
        }
        .onChange(of: appDelegate.isLaunchFinished) { finished in
            if finished {
                _vm = StateObject(wrappedValue: ViewModel())
            }
        }
    }
}

方案2:在ViewModel中补注册UserDefaults

由于UserDefaults.register(defaults:)只是设置默认值,不会覆盖已保存的用户数据,因此可以在ViewModel初始化时重复执行注册逻辑,避免依赖AppDelegate的时机:

class ViewModel: ObservableObject {
    init() {
        // 与AppDelegate中一致的默认值注册
        UserDefaults.shared.register(defaults: [
            "userDefaultItem1" : false,
            "userDefaultItem2" : 0
        ])
        // 后续访问UserDefaults即可正常获取默认值
        let item1 = UserDefaults.shared.bool(forKey: "userDefaultItem1")
    }
}

方案3:改用SwiftUI原生生命周期回调(macOS 12+)

如果可以部分替代AppDelegate的启动逻辑,使用SwiftUI的onLaunch回调(需macOS 12及以上)在ViewModel初始化前完成UserDefaults注册:

@main
struct AppName: App {
    @StateObject private var vm: ViewModel? = nil

    var body: some Scene {
        WindowGroup {
            if let vm = vm {
                ContentView()
                    .environmentObject(vm)
            } else {
                ProgressView("启动中...")
            }
        }
        .onLaunch {
            UserDefaults.shared.register(defaults: [
                "userDefaultItem1" : false,
                "userDefaultItem2" : 0
            ])
            _vm = StateObject(wrappedValue: ViewModel())
        }
    }
}

内容的提问来源于stack exchange,提问作者markb

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 02:15:36