Group Group Group Group Group Group Group Group Group

ViewModels providing input for other viewModels - best practice?

#1

I’m using the example in the final chapter as the basis for an app that has a split view controller as the root. The master view contains a table and I want the item selected by the user to drive activity in the detail view. The detail view contains a tab bar controller; each of its tabs provides the user with a selection of data derived from the item they chose.

It would seem logical for the viewModel associated with the table to expose the selected Object using a Driver and then bind this to the inputs (AnyObservable) of the viewModels associated with each of the tabs of the tab bar controller. The binding would occur in Scene enum (I’ll need to modify the signatures a bit but that doesn’t seem hard on the face of it). Would this be good practice and/or the best solution? Another possibility is to give the UITabBarController its own viewModel, bind to the tableview and then bind this to each of the tabs.

BTW, the viewModel type has been modified slightly using internal/associated Structs to clearly identify inputs, outputs and actions - doesn’t make any difference to functionality but stops me getting confused!

Ideas, guidance or comments would be greatly appreciated :slight_smile:

#2

@fpillet Can you please help with this when you get a chance? Thank you - much appreciated! :]

#3

Hi @rustprooffish,

I think what you are envisioning could work. This means that when an item is selected in the tableview, you want the selection propagated to sub-viewmodels rather than instantiating a new viewmodel every time and re-instantiating the associated ViewController.

This sounds fine to me! It perfectly fits the logic and point of Rx, where you expose to your sub-viewmodel a sequence of selected item, and the sub-vm takes action by reconfiguring itself (it could even also pass this sequence to your tabbar-vm that itself can reconfigure itself automatically).

To make this approachable, you’ll need a sink of sorts in your main VM that receives selected items (i.e. a PublishRelay<Item>). Connect the tableview’s modelSelected to the VM sink, and pass it to the submodels then you should be all set!

Remember that the best solution is the one that works smoothly for you. In this case I think it’s a good one.

Hope this helps
Florent

1 Like
#4

Thanks for the guidance Florent :smiley:

Could I just check my understanding of a sink in this context? Essentially, if I use a PublishRelay as a sink, it’s a conduit held by the “master” tab-vm that passes event from the tableview-vm to the individual tab-sub-vms? We’re using a PublishRelay as it will not complete (so it’s a good fit for UI) and it will replay the most recent event when something subscribes to it so there’s less chance of missing an event through timing issues in binding. Would that be correct?

As I may be wanting to mutate the item in one or more of these tabs, I could add the service layer to each of the viewModels by dependency injection (either using constructor injection or a dependency container), but I guess an alternative is to pass an Action between VMs instead, although thinking about it a bit more this would only really be sensible in cases when the Action is reasonably generic (e.g. deletion of the item) and the “chain” of viewModels isn’t overly long.

Cheers
Adrian

#5

@fpillet Do you have any feedback about this? Thank you - much appreciated! :]

#6

@rustprooffish when instantiating your sub-vms you’ll pass the PublishRelay so you won’t miss any event. Passing an Action for caller to catch the result(s) is a good practice I would typically use in that kind of context.

Depending on your model, you may also go for the service route where the mutation occurs by calling a service method, which will automatically reflect the change to observers via its own observables. If your app is moderatly complex, that’s how you want to handle it.

Florent