Reputation: 1225
I'm not sure if this is an antipattern in this brave new SwiftUI world we live in, but essentially I have an @EnvironmentObject with some basic user information saved in it that my views can call.
I also have an @ObservedObject that owns some data required for this view.
When the view appears, I want to use that @EnvironmentObject to initialize the @ObservedObject:
struct MyCoolView: View {
@EnvironmentObject userData: UserData
@ObservedObject var viewObject: ViewObject = ViewObject(id: self.userData.UID)
var body: some View {
Text("\(self.viewObject.myCoolProperty)")
}
}
Unfortunately I can't call self on the environment variable until after initialization:
"Cannot use instance member 'userData' within property initializer; property initializers run before 'self' is available."
I can see a few possible routes forward, but they all feel like hacks. How should I approach this?
Upvotes: 28
Views: 20925
Reputation: 79
One way to initialize ObservableObject
that depends on environment (key or object) is to pass the necessary environment from a parent view via a view init
that will itself create the StateObject
using wrappedValue:
initializer.
struct FirstScreen: View {
@Environment(\.fooService) var fooService
var body: some View {
content
.sheet(...) {
SecondScreen(
// Here we DO have access to environment
fooService: fooService
)
}
}
}
struct SecondScreen: View {
@StateObject var viewModel: ViewModel
init(fooService: FooService) {
// The ObservableObject initialisation is encapsulated here
self._viewModel = .init(
// The autoclosure initializer needs to be used
wrappedValue: .init(fooService: fooService)
)
}
}
Following is a link to my article describing the solution in more detail:
ObservableObject initialisation using Environment
Upvotes: 0
Reputation: 258461
Here is the approach (the simplest IMO):
struct MyCoolView: View {
@EnvironmentObject var userData: UserData
var body: some View {
MyCoolInternalView(ViewObject(id: self.userData.UID))
}
}
struct MyCoolInternalView: View {
@EnvironmentObject var userData: UserData
@ObservedObject var viewObject: ViewObject
init(_ viewObject: ViewObject) {
self.viewObject = viewObject
}
var body: some View {
Text("\(self.viewObject.myCoolProperty)")
}
}
Upvotes: 32