'How to initialize `var` that depends on `environmentObject` in SwiftUI View init()?
Is there a more natural way (now that it's 2022) of initializing some internal variables that depend on an @EnvironmentObject (or other @ObservedObject) within a view's init() function?
For example, the following shows what I'm trying to do (commented out) versus what works. Unfortunately the "what works" code is considerably more unwieldly (bulky, repetitive). Instead of just using a diary var I have to sprinkle my code with try? log.readDiary(for: state.now) or wrap it all in a subviews. Wondering what the best practice is.
struct NutritionView: View {
let log: LogProvider
@ObservedObject private var state: StateService
// @StateObject private var diary: DiaryReader? // init depends on using state (above)
init(log: LogProvider) {
// self.diary = try? log.readDiary(for: state.now) // would like to init here
// unfortunately `self.state` not available inside init()
}
var body: some View {
// let remaining = remainingCalories(diary: diary, goals: goals) // and use `diary` here
let remaining = remainingCalories(diary: try? log.readDiary(for: state.now), goals: goals)
VStack {
...
}
}
}
There's a related post here: Swiftui - How do I initialize an observedObject using an environmentobject as a parameter?, but I'd rather not create internal subviews to accomplish what seems to be a simple initialization. That just seems... wasteful, repetitive, unwieldly. Hoping the API has evolved a bit since then.
Solution 1:[1]
There is nothing wasteful about creating multiple View data structs which are super fast value types. We are supposed to create many small View structs that only have a small number properties used by the body. SwiftUI is recomputing these constantly and using the result of the diff to update the UILabels on the screen.
In any case, something appears to have gone wrong with your design. EnvironmentObject isn't for state, it is used for data model which is an ObservableObject class, so a reference type so its lifetime outlasts the views. Inside that we are supposed to model our data using value types, e.g. structs. We pass these into Views as lets or use @Binding for write access. We can also have funcs in our model object that can do some work and then mutate the value types (usually looking it up by ID) which results in SwiftUI detecting the change and recomputing all the View bodys that depend on the data.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | malhal |
