Sometimes we have to go through a
Binding As an environmental value. A common example of this is
binding Used to control navigation, many nested controls and actions within your app will need to read and write to this mode to change what is displayed.
Right now, we can move on
Bindings Into the environment however when we read them through
@Environment(.keyPath) We end up with the value wrapped in
Binding<Value> Type. It has some problems: First if this value binding Indicates changes The display may not be reworked, again to read / write to the value we need to use the exception pattern
myValue.wrappedValue = ...
I would like to offer an alternative solution that allows us to create views using the same syntax as we were used to
Here’s an example of how you can use the offer
@EnvironmentBinding(ToggleValueKey.self) Property cover.
struct ToggleView: View @EnvironmentBinding(ToggleValueKey.self) var enabled var body: some View HStack Toggle(isOn: $enabled) Text("Toggle") Button("Tap me") enabled = true
Note that we can pass this on to other controls using the expected
$ Prefix, and we can both read and write the value directly within the body.
Declaring an environmental value, exposing its BoundValue type.
To begin with, we are announcing a new protocol that expands the usual
EnvironmentKey, We do this so we can extract the
BoundValue Type included in
protocol EnvironmentBindingKey: EnvironmentKey where Value == Binding<BoundValue> associatedtype BoundValue static var path: KeyPath<EnvironmentValues, Value> get static var defaultBoundValue: BoundValue get
extension EnvironmentBindingKey static var defaultValue: Value .constant(self.defaultBoundValue)
Matching it is similar to
struct ToggleValueKey: EnvironmentBindingKey static var path: KeyPath<EnvironmentValues, Binding<Bool>> .toggle static var defaultBoundValue: Bool false extension EnvironmentValues var toggle: Binding<Bool> get return self[ToggleValueKey.self] set self[ToggleValueKey.self] = newValue
@EnvironmentBinding Property cover
@propertyWrapper struct EnvironmentBinding<Key: EnvironmentBindingKey>: DynamicProperty init(_ key: Key.Type) self._value = Environment(key.path) self._wrappedValue = Key.defaultValue @Environment private var value: Binding<Key.BoundValue> @Binding var wrappedValue: Key.BoundValue var projectedValue: Binding<Key.BoundValue> self.value mutating func update() self._value.update() self._wrappedValue = value self._wrappedValue.update()
Important here is
DynamicProperty A match that satisfies the
update() method. It is called by SwiftUI before the display body is evaluated, at this point we can take the opportunity to update the
@Binding An instance of the current value in