Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

swift

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?

like image 488
Arturo Avatar asked Aug 25 '21 22:08

Arturo


People also ask

What is @state and @stateobject in Swift?

@State and @StateObject use storage managed by SwiftUI that is tied to the lifetime of the view rather than a particular instance of a View struct. You can see some of how this works in the debugger. I'll use some examples from a Relay.swift app I'm working on. Relay.swift uses ObservableObject s a fair bit, and @StateObject is a natural fit.

What is the difference between @observedobject and @stateobject?

With @ObservedObject, that's true: the view will always use the object you've passed in. With @StateObject, that's not true: as we've seen above, a @StateObject property always returns the same object for the lifetime of the view. There's no way to create a StateObject that doesn't have this behavior.

Is it possible to create a stateobject manually?

There is still sometimes a use for creating a StateObject manually in your initializer. It's the same reason you may want to do it for State: if you specifically want to set an initial value based on a parameter to your view, and then let your view manage the state from there. In one of my apps, I have a search field that's initially empty.

What is the use of @stateobject in iOS counterview?

To fix this issue Apple introduced a new property wrapper called @StateObject. State object wrapper will persist the value during the renders. Update your CounterView code to use @StateObject as shown below:


2 Answers

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) }

}
like image 135
swiftPunk Avatar answered Nov 15 '22 09:11

swiftPunk


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) }

}
like image 22
ios coder Avatar answered Nov 15 '22 07:11

ios coder