12

Creating a custom environment key works, but if I wish to set the value in a view, XCODE doesn't allow it. But the predefined environment values can be set. What am I doing wrong?

struct ResetDefault: EnvironmentKey {
    static var defaultValue: Bool = false
}

extension EnvironmentValues {
    var resetDefault: Bool {
        get { self[ResetDefault.self] }
        set { self[ResetDefault.self] = newValue }
    }
}

struct ResetView: View {
    @Environment(\.resetDefault) var reset
    var body: some View {
        Text("Reset").onAppear() {
            reset = true.        // Cannot assign to property: 'reset' is a get-only property
        }
    }
}
Sid
  • 963
  • 1
  • 12
  • 17

1 Answers1

29

The Environment is used to pass values in parent > child direction, so value is set for usage. If you want to change internal of environment value then you need to wrap it somehow, possible variants are binding or reference type holder.

Here is an example of usage based on binding (similar to how .editMode and .presentationMode work)

struct TestResetEnv: View {
    @State private var isActive = false
    @State private var reset = false
    var body: some View {
        VStack {
            Text("Current: \(reset ? "true" : "false")")
            Button("Go") { self.isActive.toggle() }
            if isActive {
                ResetView()
            }
        }.environment(\.resetDefault, $reset) // set for children as env!!
    }
}

struct ResetDefault: EnvironmentKey {
    static var defaultValue: Binding<Bool> = .constant(false)
}

extension EnvironmentValues {
    var resetDefault: Binding<Bool> {
        get { self[ResetDefault.self] }
        set { self[ResetDefault.self] = newValue }
    }
}

struct ResetView: View {
    @Environment(\.resetDefault) var reset
    var body: some View {
        Text("Reset").onAppear() {
            self.reset.wrappedValue.toggle() // << change wrapped !!
        }
    }
}
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Can you think of an example where you realistically would use such an editable environment value? I wonder if this shouldn't be almost always a property on a EnvironmentObject... – Ralf Ebert May 17 '20 at 06:39
  • 1
    @RalfEbert, Realistic solution using this approach [SwiftUI - How to close the sheet view, while dismissing that view](https://stackoverflow.com/a/60314365/12299030) – Asperi May 17 '20 at 06:45
  • @RalfEbert, another one using reference type [SwiftUI how to change state on keyUp macOS event?](https://stackoverflow.com/a/61278596/12299030) – Asperi May 17 '20 at 06:48
  • This solution compiles but doesn't work. The wrapped value is never set any differently from it's default. – johnrubythecat Nov 22 '20 at 01:42
  • Actually, correction: good solution, you have to remember to add : .environment(\.resetDefault, $reset) (or else no warnings or errors, but nothing happens) – johnrubythecat Nov 22 '20 at 01:52