Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

ios

swift

swiftui

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?
}
like image 822
M1X Avatar asked Oct 26 '22 20:10

M1X


2 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).

like image 180
pawello2222 Avatar answered Oct 29 '22 15:10

pawello2222


Just noting that I'm using the NestedObservableObject approach from How to tell SwiftUI views to bind to nested ObservableObjects.

Normally I'd avoid this but the nested object in question is actually a CoreData model so breaking things out into smaller views doesn't really work in this regard.

Since the world treats NSManagedObjects as (mostly) ObservableObjects, this solution seemed best.

like image 33
Michael Long Avatar answered Oct 29 '22 15:10

Michael Long