Home > Enterprise >  @StateObject vs @ObservedObject when passed externally but owned by the view
@StateObject vs @ObservedObject when passed externally but owned by the view

Time:02-12

Based on this answer: What is the difference between ObservedObject and StateObject in SwiftUI

And the Apple documentation code here: https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

In SwiftUI app, a @StateObject property wrapper should be used when a View instantiates the object itself, so that the object won't be recreated during a view update.

If the object is instantiated somewhere else, an @ObservedObject wrapper should be used instead.

However, there is a fine line which makes it a bit unclear: what if the object is instantiated somewhere else, but "injected" to the View and then the view is the sole owner / holder of that object? Should it be @StateObject or @ObservedObject?

Sample code to get the point illustrated:

import SwiftUI
import Combine
import Foundation


struct ViewFactory {
    func makeView() -> some View {
        let viewModel = ViewModel()
        return NameView(viewModel)
    }
}


final class ViewModel: ObservableObject {
    @Published var name = ""
    init() {}
}


struct NameView: View {

    // Should this be an `@ObservedObject` or `@StateObject`?
    @ObservedObject var viewModel: ViewModel

    init(_ viewModel: ViewModel) {
        self.viewModel = viewModel
    }

    var body: some View {
        Text(viewModel.name)
    }
}

Based on this article: https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-observedobject-state-and-environmentobject

There is one important difference between @StateObject and @ObservedObject, which is ownership – which view created the object, and which view is just watching it.

The rule is this: whichever view is the first to create your object must use @StateObject, to tell SwiftUI it is the owner of the data and is responsible for keeping it alive. All other views must use @ObservedObject, to tell SwiftUI they want to watch the object for changes but don’t own it directly.

it appears that if the View to instantiate the ViewModel, it has to be declared with @StateObject. My code is very similar, the only difference is that the ViewModel is created elsewhere, but the View "owns" it after the initialization.

CodePudding user response:

This is a really interesting question. There's some subtle behavior going on here.

First, notice that you can't just change @ObservedObject to @StateObject in NameView. It won't compile:

struct NameView: View {
    @StateObject var viewModel: ViewModel

    init(_ viewModel: ViewModel) {
        self.viewModel = viewModel
        //   ^            
  • Related