'How to initialize StateObject within SwiftUI with parameter

I'm working on a SwiftUI app using the MVVM architecture. I have the problem that I need to pass data from a parent View into its child viewModel, but I'm not sure how to pass a parameter from the view into the viewModel.

The child View which should own the viewModel is here:

struct InstanceView: View {
    @ObservedObject var instance: Instance
    
    @StateObject var viewModel = InstanceViewViewModel(instance: instance)

    var body: some View {
        ...
    }
}

And this is the viewModel:

class InstanceViewViewModel: ObservableObject {
    @ObservedObject var instance: Instance
    
    ...
}

Obviously the View doesn't work, I get the error Cannot use instance member 'instance' within property initializer; property initializers run before 'self' is available

If I try using init() in the View to assign a value to viewModel:

@ObservedObject var instance: Instance

@StateObject var viewModel: InstanceViewViewModel

init(instance: Instance) {
    self.instance = instance
    self.viewModel = InstanceViewViewModel(instance: instance)
}

But I get the error Cannot assign to property: 'viewModel' is a get-only property.

I have read that you should give the viewModel property (instance) a default value, and then set it in the View with .onAppear(). However, in my case, Instance is a Core Data object so I can't really create one to use as a default value.

I also read that I could maybe use _instance = StateObject(wrappedValue: InstanceViewViewModel(instance: instance)) in the View's init, and swiftUI would be smart enough to only initialize the StateObject once. However I'm pretty sure this is a bad practice.

I also can't use lazy on a StateObject

So is there any other way to achieve this? Could a solution be somehow getting the value InstanceView is being initialized with on this line:

@StateObject var viewModel = InstanceViewViewModel(instance: instance)

, outside of an init, so I don't need to reference self, like you would do inside of an init? (this is probably a stupid idea)

Or should I be implementing MVVM in a different way?

Thanks!



Solution 1:[1]

SwiftUI property wrappers are often automagic. @StateObject modifies the property in a way that the initialization has to be at the point of declaration. The pattern suggested by Apple is to create the @StateObject in the parent of the view and pass it down either as an @ObservedObject or @EnvironmentObject.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Морт