Home > Net >  ViewModel executes before AppDelegate
ViewModel executes before AppDelegate

Time:11-28

I am wondering if there is any way that can solve the ViewModel executing before the AppDelegate in my macOS app.

Suppose I have these files:

AppDelegate.swift

final class AppDelegate: NSObject, NSApplicationDelegate {
  func applicationDidFinishLaunching(_ notification: Notification) {
    UserDefaults.shared.register(defaults: [      // <--- breakpoint 1
      "userDefaultItem1" : false,
      "userDefaultItem2" : 0
    ])
  }
}

AppName.swift

@main
struct AppName: App {
  @NSApplicationDelegateAdaptor(AppDelegate.self)
  private var appDelegate   // <--- breakpoint 2
  
  @StateObject
  var vm = ViewModel()      // <--- breakpoint 3
  
  var body: some Scene {
    WindowGroup {
      ContentView()
       .environmentObject(vm)
    }
  }
}

When I run the application, it hits breakpoint 2, then breakpoint 3, then breakpoint 1.

As a result, none of the UserDefaults are registered prior to access in the ViewModel and it crashes.

Is this something that is supposed to happen, and though the use of AppDelegates are not required, there is use in legacy code that is now moved over to SwiftUI.

CodePudding user response:

if you want to make sure that something is processed before even AppDelegate gets its delegate methods called then you could also place UserDefaults code in init of your AppDelegate. Or also possible, simply move the code over to applicationWillFinishLaunching which is called earlier.

init() { .. } will work because NSObjects always follow the alloc-init pattern. So init gets called first (second after alloc) before even any delegate method is called.

Also when your App crashes when UserDefaults are missing, then it is worth testing what happens when there are no UserDefaults used at all. This will force you to code a fallback and then you apply UserDefaults onto those prepared fallback properties "refreshing" them and go on from there. That makes sure when a User opens your app the first time, where most likely there are no UserDefaults set, it will not crash.

In short: using UserDefaults without fallback is a design pattern issue. You should always provide evaluation of UserDefaults before applying and sometime even before changing them, because Users can even erase them or otherwise manipulate them on a mac.

PS: your ViewModel is called very likely via an entry in Storyboard or main nib and is accordingly marked in Info.plist to do so.

another point to think about is, when this particular UserDefault is needed for the ViewModel then it is also worth to recall and evaluate them in init() of the ViewModel instead of AppDelegate

  • Related