swiftswiftui

Is it bad to directly pass @StateObject's into into child views?


I realize that @StateObject is deprecated in favor of Observable. However if I were to be in a codebase that was trying to limit themselves to old versions of iOS I would like to understand this better.

@MainActor
class ViewModel: ObservableObject {
    // @Published properties here
}

struct MainView: View {
   @StateObject var viewModel: ViewModel = ViewModel()

   var body: some View {
      SubView(viewModel: viewModel)
   }
}

struct SubView: View {
   @StateObject var viewModel: ViewModel
}

As I understand it there is nothing wrong with doing it this way. I could use a binding but that is a bit overkill considering that ViewModel is a reference type.

What I am hoping to understand is what if SubView had something it needed to do in its init method? How would you write passing in that ObservedObject properly.

struct SubView: View {
    @StateObject var viewModel: ViewModel
    init(viewModel: ViewModel) {
       self.viewModel = viewModel // ERROR: Cannot assign to property: 'viewModel' is a get-only property
       // Something else important you needed to do in init like make a string de-bouncer
    }
}

Even if you use self._viewModel you get an error. These errors make sense... How to resolve them... Not sure.


Solution

  • StateObject is a source of truth, ObservedObject and EnvironmentObject are for passing around.

    The SubView should be @ObservedObject.

    The main difference between StateObject and ObservedObject is lifecycle.

    @StateObject is assigned "storage" within SwiftUI that survives View reinitialization, which can happen at any time.

    @ObservedObject does not have the ability to survive redraws and without a source of truth like StateObject or an object from FetchRequest the objects get recreated and often cause memory leaks.