Home > database >  Prevent initializing ObservedObject twice between views
Prevent initializing ObservedObject twice between views

Time:10-21

So I am still learning Swift and I can't seem to understand how to get around initializing an ObservedObject twice, so I wanted to see if I can get some help from the community.

So I have my App file here:

@main
struct MapGlider: App {
    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
    @ObservedObject var mainViewModel = MainViewModel()
    var body: some Scene {
        WindowGroup {
            MainScreenContainer(
                showOnboarding: $mainViewModel.showOnboarding
            )
        }
    }
}

Which has my @ObservedObject var mainViewModel = MainViewModel() listed inside.

Then inside MainScreenContainer, I have the following items:

struct MainScreenContainer: View {
    @ObservedObject var mainViewModel = MainViewModel()
    @Binding var showOnboarding: Bool
    
    var body: some View {
        if showOnboarding {
            OnboardingView(showOnboarding: $showOnboarding)
        } else {
            MainView()
                .environmentObject(mainViewModel)
        }
    }
}

The problem with that is, inside my MainViewModel(), I am initializing it twice on start, which gives me the following out:

2022-10-20 16:48:37.657849-0500 MapGlider[744:46395] Initialize: MainViewModel()
2022-10-20 16:48:37.745881-0500 MapGlider[744:46395] Initialize: MainViewModel()

Here is the sample code inside my MainViewModel() observed object:

class MainViewModel: ObservableObject {

    // MARK: - APP STORAGE
    @AppStorage("showOnboarding") var showOnboarding = true

    // MARK: - INIT
    init() {
        log.info("Initialize: MainViewModel()")
    }
}

Is this normal behavior? If not, how do I prevent it being initilized twice on app start? How do I pass down the ObservedObject to other views without re-initializing it each time as I'm using MainViewModel() inside MainView() also.

CodePudding user response:

try this approach, passing the one source of truth, @StateObject var mainViewModel = MainViewModel() to other views using @EnvironmentObject :

@main
struct MapGlider: App {
    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
    @StateObject var mainViewModel = MainViewModel()  // <-- here
    
    var body: some Scene {
        WindowGroup {
            MainScreenContainer().environmentObject(mainViewModel)  // <-- here
        }
    }
}

struct MainScreenContainer: View {
    @EnvironmentObject var mainViewModel: MainViewModel  // <-- here

    var body: some View {
        if mainViewModel.showOnboarding { // <-- here
            OnboardingView(showOnboarding: $mainViewModel.showOnboarding) // <-- here
        } else {
            MainView()
        }
    }
}
  • Related