swiftswiftuiobservableios17

How to bind environment variable ios17


With the new @Observable macro introduced for iOS 17, we can now use environment objects in the following way

@Environment(MyType.self) var myTypeObj

Now if myTypeObj has some property, call it myProperty which I'd like to pass as binding somewhere using $ syntax, Swift complains that it cannot find the variable. For example,

Picker("My Picker", selection: $myTypeObj.myProperty) { // we get an error on this line
  ForEach(1 ... 50, id: \.self) { num in
    ...
  }
}

we get an error saying Cannot find $myTypeObj in scope. Is this a Swift bug or am I doing something wrong?


Solution

  • In iOS 17 you have to use Bindable.

    @Observable
    class AppState {
        var isOn: Bool = false
    }
    
    struct ContentView: View {
        
        @Environment(AppState.self) private var appState
        
        var body: some View {
            
            @Bindable var bindable = appState
            
            VStack {
                Toggle("Is On", isOn: $bindable.isOn)
            }
            .padding()
        }
    }
    
    #Preview {
        ContentView()
            .environment(AppState())
    }
    

    in main App:

    import SwiftUI
    
    @main
    struct myApp: App {
    
        private var appState = AppState()
    
        var body: some Scene {
            WindowGroup {
                ContentView()
                    .environment(appState)
    
            }
        }
    }