Instruction

You’ve learned two ways to implement one-way binding between views and a view model, where a view needs only read-access to a view model object. The next question is, what do you need to do to get two-way binding, where a view can update a value in the view model object? Consider these three cases.

@State in View

You declare and instantiate the view model object in the view. Then somewhere in this view, you want to modify a value in the view model object. The easiest case is to use a binding to the view model object. For example, the view model object has a collection of objects that the view displays in a List, and you want to modify a property of an object in the list:

@State var store = TheMetStore()
...
List($store.objects, id: \.objectID) { $object in
  ...
  object.isFavorite.toggle()

Passed to View

You declare and instantiate the view model object in a view or in the app struct, then pass it as a parameter to a subview. In the subview, declare the view model object as @Bindable:

// In TheMetApp
struct TheMetApp: App {
  @State var store = TheMetStore()
  var body: some Scene {
    WindowGroup {
      ContentView(store: store)
    }
  }
}

// In ContentView
struct ContentView: View {
  @Bindable var store: TheMetStore
  ...
  List($store.objects, id: \.objectID) { $object in
    ...
    object.isFavorite.toggle()

@Environment

You declare and instantiate the view model object in the app struct or in a view, then inject it into the environment of the app or the parent view’s subtree. An environment object is read-only, but you can create a @Bindable version of it in the view:

// In TheMetApp
struct TheMetApp: App {
@State var store = TheMetStore()
  var body: some Scene {
    WindowGroup {
      ContentView()
        .environment(store)
    }
  }
}

// In ContentView
@Environment(TheMetStore.self) var store
...
  VStack {
    @Bindable var twoWayStore = store
    ...
    List($twoWayStore.objects, id: \.objectID) { $object in
    ...
      NavigationLink(object.title) {
        ObjectView(object: $object)   // ObjectView modifies object.isFavorite
      }

Follow along with the demo video to see all of these in action.

See forum comments
Download course materials from Github
Previous: Demo 1 Next: Demo 2