SwiftUI Is it ok to put an ObservableObject inside another ObservableObject?

Viewed 3911

I have a view called PurchaseView. This view displays details about the purchase, what was purchased and who purchased it. What I'm doing is that in this view im putting both the ItemView and ClientView inside PurchaseView. ItemView and ClientView are shared and there are used in other parts of my app. They have their own ViewModels.

I have also tried to put ItemViewModel and ClientViewModel inside PurchaseViewModel but I do not know if it is ok to put an ObservableObject inside another ObservableObject. Is this a good approach or there should not be any ObservableObject inside an ObservableObject? Which one of the following is better?

This?

class PurchaseViewModel: ObservableObject {
    let clientViewModel: ClientViewModel
    let itemsViewModel: ItemViewModel

    //
}

Or this?

struct PurchaseView: View {
    @ObservedObject var purchaseViewModel: PurchaseViewModel
    @ObservedObject var itemViewModel: ItemViewModel
    @ObservedObject var clientViewModel: ClientViewModel

    var body: some View {
        //
    }
}

Purchase model:

class Purchase {
    let id: String
    let total: Double
    // ...
    var item: Item?
    var client: Client?
}
1 Answers

Your first solution will not work as the changes to the nested ObservableObjects aren't propagated upwards:

class PurchaseViewModel: ObservableObject {
    let clientViewModel: ClientViewModel
    let itemsViewModel: ItemViewModel
    ...
}

A workaround can be found here: How to tell SwiftUI views to bind to nested ObservableObjects.


Your second approach is right and will work for most cases:

struct PurchaseView: View {
    @ObservedObject var purchaseViewModel: PurchaseViewModel
    @ObservedObject var itemViewModel: ItemViewModel
    @ObservedObject var clientViewModel: ClientViewModel
    ...
}

If you share an ObservableObject for many views you can inject it to the environment instead and access as an @EnvironmentObject.


Alternatively you can make your nested ObservableObjects to be structs:

class PurchaseViewModel: ObservableObject {
    @Published var clientViewModel: ClientViewModel // <- add `@Published`
    ...
}
struct ClientViewModel { // <- use `struct` instead of `class`
    ...
}

Note that your ClientViewModel will become a new struct every time it (or any of its properties) changes - this solution shouldn't be overused (especially for complex ViewModels).

Related