View Models & Environment Property Instruction

Using @Environment & ViewModels

In Lesson 2, you saw how @State works with @Observable to implement the MVVM architecture. You can declare and instantiate your view model object as a @State property in a view, then pass it to a subview. That subview doesn’t need any property wrapper if it’s only reading values from the view model object.

Now, suppose that subview doesn’t need access to the view model object, but one of its subviews does. Do you pass the view model object to subview A only so it can pass it to subview B? It’s not a big deal when you’re just skipping one level, but in many SwiftUI apps, the view hierarchy can be deeply nested. You don’t want to be passing view model objects down the hierarchy just to reach one or two subviews that need it. Instead, you can inject an instance of the view model object into your app’s environment, and then any subview that needs it can retrieve it from the environment.

This also works in a subtree of an app’s views. Inject the view model object into one view and all of its child views can retrieve it.

Here’s how you do it. You instantiate the view model object as a @State property, then, instead of passing it to a subview as a parameter, you attach it with the .environment modifier:

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

From here, any view in the subtree can access the view model by declaring it with the @Environment property wrapper, specifying the type of the environment object:

@Environment(TheMetStore.self) var store

Note: You don’t have to name it the same in every view.

To stop the view’s preview from crashing, also attach a view model to the view in its preview:

#Preview {
  ContentView()
    .environment(TheMetStore())
}

Follow along with the demo video to see this in action.

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