MVVMÇ (MVVM Calçot) is an implementation of MVVM influenced by VIPER. Basically VIPER with MVVM names.
The most important concept of MVVMÇ is that implements MVVM with immutable ViewModels, this helps to write correct and testable code with minor complexity.
Other than this it introduces Interactor and Coordinator to MVVM.
Models are entities that represent the data behind the business logic. In order to have immutable ViewModels the model needs to be immutable as well, preferably using value types when possible.
The Model doesn't contain any logic but is just the input of a ViewModel that transforms the model into what a View needs.
The View is what the user actually see, usually a UIView or UIViewController. View should never have any logic but instead use whaveter comes from the ViewModel directly without manipulation. Because ViewModels should be immutable the View might have to hold the state.
For example, a UISwitch, is already holding the state on/off so there is no need to replicate it in the ViewModel or the Model with the risk of inconsistencies. Then the View will pass the value to the ViewModel whenever needed.
Let's say a ViewModel needs to provide a String based on the switch value.
Instead of:
viewModel.notificationsEnabled = notificationsSwitch.isOn
descriptionLabel.text = viewModel.descriptionYou would use:
descriptionLabel.text = viewModel.description(notificationsEnabled: notificationsSwitch.isOn)There are few cases where this is not possible (Drawbacks of Immutable ViewModels)
The ViewModel contains all the business logic and the presentation logic. It transforms the Model into what the View should display. In MVVMÇ the goal is to have always immutable ViewModels because it makes it really easy to tests them. Given a Model you will always have the exact same output.
The ViewModel forwards updates of the Model or other actions to the Interactor and forwards navigation to the Coordinator. The View never navigates or use the Interactor or Coordinator directly, but instead it delegates to the ViewModel that then dispatches it as necessary.
The biding between the View and the Interactor takes care of replacing the ViewModel whenever the Model changes.
Example:
struct User {
let name: String
let nickname: String
....
}
struct UserCellViewModel {
let user: User
var title: String {
return "\(user.name) @(\(user.nickname))"
}
...
}The Interactor is responsible to manipulate the model, keep it in a consistent state, notify changes.
The interactor can be used, for example, to communicate with your backend to fetch the model, to update it, etc.
The Coordinator is responsible to setup all the components of a module together and eventually to use other Coordinator's to navigate.
It holds a weak reference to the View because the View itself is what keeps all the compents alive until the View is removed from the hierachy.
The Interactor is binded to the View so that whenever the Model changes a new ViewModel is created and the View is reconfigured.
The View has a single method that given a ViewModel updates the whole UI (configure(with: viewModel)).
This means that given the same ViewModel the View is supposed to looks exactly the same.
Because of immutability of the ViewModel and the fact that given the same Model the ViewModel is always going to behave the same way; and given the same ViewModel the View is going to look the same, we can easily test all the components.
ViewModel can be unit tested by using an arbitrary Model.
View can be snapshot tested by using an arbitrary ViewModel.
Interactor can be tested mocking its dependencies and veryfing its Model.
There are few cases where immutability of the ViewModel might not work.
One is forms that use UITableView, since tableView's cells are reused the state can't be kept in the View or it will be lost once the cell is reused. However in our experience is better to not use UITableView for building forms and reusing cells cause more troubles than gains, UIStackView is a better choice for simple forms.
Another case is with animations, this need to be solved case by case by escaping a bit from immutability.
At Verse for example to properly animate the like button of a cell we had to keep the liked state of each Feed entry mutable and using callbacks to update the cell state.
This is needed to avoid re-creating the whole ViewModel since it would reload the whole table.
MVVMÇ was initially developed by Alex Manzella and Joan Romano.
It was introduced at @Verse and refined together with Pol Quintana and Ramon Arguello.
The Verse app was completely rewritten using MVVMÇ.
At @Runtastic, were the architecture was introduced, has been adopted since end of 2016.
You might contact one of these people for any question about it.
