Search code examples
swiftswiftui

Accessing StateObject's object without being installed on a View. This will create a new instance each time - SwiftUI


I have to do some initial setup each time the app starts, but I'm getting the error:

enter image description here

The error is clear, the answer is not. I tried putting the init in a subview, but I can't, it needs to be in the root @main. This is how I have it defined:

@StateObject private var amplifyConfig: AmplifyConfig = AmplifyConfig()

init() {
    if(amplifyConfig.isAmplifyConfigured == false) {
        amplifyConfig.dataStoreHubEventSubscriber()
        amplifyConfig.configureAmplify()
    }
}

How do I get rid of that warning and actually implement it so it doesn't create multiple instances, at the end of the day that's why I'm using @EnvironmentObject for?


Solution

  • You cannot access any value before they get initialized, use onAppear():

    import SwiftUI
    
    @main
    struct YourApp: App {
        
        @StateObject private var amplifyConfig: AmplifyConfig = AmplifyConfig()
        
        var body: some Scene {
            
            WindowGroup {
                ContentView()
                    .onAppear() {
                        if (!amplifyConfig.isAmplifyConfigured) {
                            amplifyConfig.dataStoreHubEventSubscriber()
                            amplifyConfig.configureAmplify()
                        }
                    }
            }
        }
    }
    

    Update: An actual use case

    import SwiftUI
    
    @main
    struct YourApp: App {
        
        @StateObject private var amplifyConfig: AmplifyConfig = AmplifyConfig()
        
        @State private var isLoaded: Bool = Bool()
        
        var body: some Scene {
            
            WindowGroup {
                
                VStack {
                    if (isLoaded) { ContentView() }
                    else { Text("Loading . . .") }
                }
                .onAppear() {
                    if (!amplifyConfig.isAmplifyConfigured) {
                        amplifyConfig.dataStoreHubEventSubscriber()
                        amplifyConfig.configureAmplify()
                        completionHandler { value in isLoaded = value }   
                    }
                    else {
                        isLoaded = true  
                    }
                }
            }   
        }
    }
    
    
    func completionHandler(value: @escaping (Bool) -> Void) {
        
        // Some heavy work here, I am using DispatchQueue.main.asyncAfter for replicating that heavy work is happening! But you use your own code here.
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(3000)) { value(true) }
    
    }