Initialising ObservableObject inside View's body

Hello,

I was browsing through book and one thing caught my attention in ContentView. You are initialising AnimalsNearYouViewModel inside View body there which is ObservableObject and holds some state in @Published properties.

So each time location changes and ContentView’s body gets called you will make new instance of AnimalsNearYouViewModel and reset it’s state.

Am I missing something here? If not I think this is not desired behaviour. In my projects I never initialise ViewModels inside View body because in general it can cause very undesirable behaviour. Also it will make lot of heap allocations since each time new reference type is created that is also performance hit.

struct ContentView: View {
  let managedObjectContext = PersistenceController.shared.container.viewContext
  @StateObject var locationManager = LocationManager()

  var body: some View {
    TabView {
      AnimalsNearYouView(
        viewModel: AnimalsNearYouViewModel(
          animalFetcher: FetchAnimalsService(
            requestManager:
              RequestManager()
          ),
          animalStore: AnimalStoreService(
            context: PersistenceController.shared.container.newBackgroundContext()
          )
        )
      )
      .tabItem {
        Label("Near you", systemImage: "location")
      }
      .environment(\.managedObjectContext, managedObjectContext)

      SearchView()
        .tabItem {
          Label("Search", systemImage: "magnifyingglass")
        }
        .environment(\.managedObjectContext, managedObjectContext)
    }
    .environmentObject(locationManager)
  }
}

Hello, @djuka777 .

Thank you for much for reading the book and noticing this.

You’re right. Usually, you’d want to use a StateObject to retain the state of your view model. But in this case, our intentions through the book is that while the reader builds the app they’d pass through the two different approaches (using ObservaleObject at a time of the project that there was no problem using it) and StateObject in another feature.

As the project grew, and LocationManager was introduced, the app would adapt and be refactored to account for that. However, notice that as LocationManager changes, it calls the body of ContentView and AnimalsNearYouView, even if you use a StateObject. SwiftUI is smart enough to know where LocationManager is a dependency and updates only that part of the body, without affecting the state that didn’t change.

Thanks for noticing that! We’ll note this and you can look for a fix in the next editions of the book. :]

1 Like