I have to do some initial setup each time the app starts, but I'm getting the error:
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?
@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.
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.
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.
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:
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()
}
}
}
}
}
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) }
}
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()
}
}
}
}
}
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) }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With