Why we really need MVCVM not just MVVM in iOS

I don’t know why it took this long, but it finally clicked.

I’ve been creating view models for my views but I’ve been quite confused as to what to call the larger class that embodies the view models for the entire view. Let's look at the example below.

ViewController -> ViewControllerViewModel

    DetailView -> DetailViewModel

    TableView -> [CellViewModel]

    GraphView -> GraphViewModel

The ViewControllerViewModel will encapsulate the view models for all the other smaller views.  However, in the past, I would also have this ViewControllerViewModel handle fetching data, updating the view model, telling the view to update itself.  Although it split my UIViewController code in half, it still could have a better separation of concerns.  So let's look at each of it's responsibilities and see if we can split them up a bit better.

The view model simply transforms the model data into something consumable by the view. The funny thing is that you’re going to need a larger container for all your little view models, especially when you’re dealing with tables views and collection views.  This larger view model is well, still a view model!  Just like a larger model made up of many smaller models is still a model!

Now we get a little into controller land when we think about the fact that the larger view models are often the ones constructing the smaller view models.  For instance, in a UITableView, you’re going to need to take the data you get from the models and coerce it into the view model.  It seems to follow that you could have a method on each of the smaller view models that helps coerce this data just like you do for model objects being coerced from JSON.

So you really should be able to just call the following:

let cellViewModels = models.map{cellViewModel($0)}

That’s super simple.  So there remains a question, where do you handle the effects that the input has on the data?

Up until now, I’ve passed the result of any picker to the view controller then to the view model and let it take care of the rest.  Holy crap that’s a waste.

You simply need to have your date picker and metric picker delegates to be a separate controller that the view has access to.  This controller than can manipulate the ViewModel and then tell the view to update itself.

I really think this pattern is more like MVCVM.

Should a VM update a display? Certainly not, that’s a controller's job.

Should a VM hit the web and then create itself? Nope, still a controller’s job

Should a VM update itself when the view registers user input? Nope the controller should get that input, update the ViewModel and then update the view.

This allows view models to be reusable with their views.  The views and the vie w models are coupled, but they can be used anywhere, as long as you have a controller to coordinate them.

The last piece is that any view should be able to be instantiated by doing the following:

DetailView(viewModel:detailViewModel)

Simple enough right? So thinking about the view structure from before.

ViewController
    DetailView
    TableView
    GraphView

If we want a view that is setup like that using the MVCVM convention, it might look a little bit like the following:

class AwesomeVC: UIViewController {
    var controller:Controller!
      
    override func viewDidLoad( ) {
        super.viewDidLoad( )
        loadData( )   
    }

    func loadData( ) {
        spinner.start( )
        controller.loadData( ).then {
            self.spinner.stop( )
        }.error {
            self.spinner.stop( )
            presentErrorMessage(error)
        }
    }
}

protocol Controller {
    var viewDelegate:ViewDelegate {get set}
    func loadData( ) -> Promise<Void>
}

class AwesomeController: Controller {
    var vcvm:ViewControllerViewModel?
    var viewDelegate:ViewDelegate

    init(viewDelegate:ViewDelegate) {
        self.viewDelegate = viewDelegate        
    }

    func loadData() -> Promise<Void> {
        return getModel( ).then { model in
            vcvm = ViewControllerViewModel(model: model)
            Dispatch.async {
                viewDelegate.updateView( )
            }
        }
    }
}

struct ViewControllerViewModel {
    var detailViewModel:DetailViewModel
    var cellViewModels:[CellViewModel]
    var graphViewModel:GraphViewModel    

    init(model: model) {
        detailViewModel = DetailViewModel(model: model)
        cellViewModels = model.array.map{cellViewModel($0)}
        graphViewModel = GraphViewModel(model: model)
    }
}

It's not using reactive bindings, it simply would use a delegate to update the view.  Also I'm using promises instead of closures to handle callbacks. Curious to hear your thoughts on this pattern.