I was trying to use the SwiftUI @Environment
property wrapper, but I can't manage to make it work as I expected. Please, help me understanding what I'm doing wrong.
As an example I have an object that produces an integer once per second:
class IntGenerator: ObservableObject {
@Published var newValue = 0 {
didSet {
print(newValue)
}
}
private var toCanc: AnyCancellable?
init() {
toCanc = Timer.TimerPublisher(interval: 1, runLoop: .main, mode: .default)
.autoconnect()
.map { _ in Int.random(in: 0..<1000) }
.assign(to: \.newValue, on: self)
}
}
This object works as expected since I can see all the integers generated on the console log. Now, let's say we want this object to be an environment object accessible from all over the app and from whoever. Let's create the related environment key:
struct IntGeneratorKey: EnvironmentKey {
static let defaultValue = IntGenerator()
}
extension EnvironmentValues {
var intGenerator: IntGenerator {
get {
return self[IntGeneratorKey.self]
}
set {
self[IntGeneratorKey.self] = newValue
}
}
}
Now I can access this object like this (for example from a view):
struct TestView: View {
@Environment(\.intGenerator) var intGenerator: IntGenerator
var body: some View {
Text("\(intGenerator.newValue)")
}
}
Unfortunately, despite the newValue
being a @Published
property I'm not receiving any update on that property and the Text
always shows 0. I'm sure I'm missing something here, what's going on? Thanks.
Although declared in the same way as observable objects, environment object bindings are declared in SwiftUI View files using the @EnvironmentObject property wrapper. Before becoming accessible to child views, the environment object must also be initialized before being inserted into the view hierarchy using the environmentObject () modifier.
SwiftUI offers four options for implementing this behavior in the form of state properties, observable objects, state objects and environment objects, all of which provide the state that drives the way the user interface appears and behaves. In SwiftUI, the views that make up a user interface layout are never updated directly within code.
In SwiftUI, most of these system-wide settings and variables are accessible via the @Environment property wrapper. You can see all the available options in EnvironmentValues. To access environment values, you create a @Environment variable specifying a keypath to value you want to read and write.
In SwiftUI, the views that make up a user interface layout are never updated directly within code. Instead, the views are updated automatically based on the state objects to which they have been bound as they change over time. This chapter will describe these four options and outline when they should be used.
Environment
gives you access to what is stored under EnvironmentKey
but does not generate observer for its internals (ie. you would be notified if value of EnvironmentKey changed itself, but in your case it is instance and its reference stored under key is not changed). So it needs to do observing manually, is you have publisher there, like below
@Environment(\.intGenerator) var intGenerator: IntGenerator
@State private var value = 0
var body: some View {
Text("\(value)")
.onReceive(intGenerator.$newValue) { self.value = $0 }
}
and all works... tested with Xcode 11.2 / iOS 13.2
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