简体   繁体   中英

SwiftUI 2 View Models in a View

I have this view which takes as a param its own View Model. Inside this view I need to show another view which in order for it to be displayed, needs its own View Model. So I present the Purchase view and then inside the Purchase view I need to display the details of the actual item which was purchased. So I pass as a parameter also the ItemViewModel . Both PurchaseViewModel and ItemViewModel are used on other views also.

It could happen in the feature that I would also need ClientViewModel which would be used to display data about the actual buyer of this item. Does this mean that I would need to pass also @ObservedObject var clientViewModel: ClientViewModel as a parameter?

My question is: Is this approach good or there is a better way to do this?

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

    var body: some View {
        VStack {
            Text(purchaseViewModel.title)
            ItemView(itemViewModel: itemViewModel)
        }
    }
}

class Purchase {
    let senderID: String
    let receiverID: String
    var status: PurchaseStatus
    var item: Item?
}

You could make the PurchaseViewModel contain the ItemViewModel and ClientViewModel as properties in the same way that the PurchaseView contains the ItemView and ClientView .

That would make PurchaseViewModel responsible for creating the instances of the other view models. The parent of PurchaseView would no longer need to know about the implementation of PurchaseView .

You should start by considering the relationship of the models , not the view models .

For example, you could make a case that a Purchase involves one or more items ( Item ) and a purchaser ( Client ). In this case you would actually create a model something like:

class Purchase {
    var items = [Item]()
    var client: Client
    ...
}

And your PurchaseViewModel would reflect this, containing the ItemViewModel and ClientViewModel

    let clientViewModel: ClientViewModel
    let itemsViewModels: [ItemViewModel]

    init(purchase: Purchase) {
        self.clientViewModel = ClientViewModel(client:purchase.client)
        self.itemsViewModels = purchase.items.map { ItemViewModel(item:$0) }
    }

If you have a hierarchy of views that need some unrelated models then you can consider using the environment, especially for models that are "global"

For example, say that the user was looking at an item detail, but they were not yet making a purchase (So there is no Purchase yet) and you want to have a button that shows them their Client detail.

You could pass the ItemViewModel explicitly to the ItemDetailView :

struct ItemListView {
   ...

   Button(action: { ItemDetailView(item:someItemView) }) 
       { ... }

}


struct ItemDetailView {
    ...
    Button(action: { ClientView() }) {
        Text("Client detail")
    }
}

struct ClientDetailView {

   @EnvironmentObject client: ClientViewModel
   ...
}

You can inject the ClientViewModel into the environment at a suitable point, such as the root view or even scene delegate.

If you have a hierarchy of views that need these models, but not all views need all models, you could use the environment to provide the various models rather than directly injecting them via initialiser parameters.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM