Merging all iOS architecture benefits: know the VIP-VM architecture

As explained in a few articles before, iOS has a lot of good and viable architectures and design patterns to allow your app to be kept, redesigned and to grow in the long term. Each architecture has its own benefits and disadvantages forcing you to choose which one best fits your project's business rules and its objectives as long as how your different screens are going to establish a communication. Thinking about all of them, I designed myself a new solution for a big project, with a totally responsive UI, a bunch of different kinds of interactions, and a lot of complex business rules in each screen or accessory. I call it the VIP-VM(ViewModel) clean architecture where I placed all the many advantages from the MVVM, VIPER and VIP architectures and just a few weak points.

Reviewing other architectures particularities

1)MVVM

MVVM and its variations are known by holding a central layer known as the ViewModel, which is responsible for holding all the content that should be presented in the UI(View/ViewController) and some business logic. This main layer receives some content from other places, like an API, and keeps all the data ready to be easily accessed by the View, which shall be updated. The UI updates happen when some of the ViewModel data is changed, making the two layer independent and the interface totally reactive.

I like to see the ViewModel like a complementary sublayer for the View.

2)VIPER

VIPER is more complete since it has a layer just for business rules and use cases, which is called Interactor, but besides it has a Presenter which responds for user interactions and provides some content for the View, working like it was a ViewModel. VIPER inherits some MVVM features with that, since the communications is bidirectional.

The UI is still reactive since the Presenter must keep all the content consumed by the UI and listen to its interactions. Usually, in this way, you can also have the View's subcomponents delegates and data sources in the Presenter, not even needing to pass through the ViewController. This is a great advantage. Also, it's not necessary to pass through the Interactor if there is no use case to be executed, sometimes just a layout change not based on business rules is needed(Ex: textfield masks and formatting). However, as said in the previous article, the Presenter can be too much coupled sometimes and it's not trivial to split responsabilities.

3)VIP

VIP and its variations always consist on a cycle with the three same layers from the VIPER. Interactor listens to user interactions, executes some business rules and pass its output to the Presenter, which transforms that into some data ready to be consumed by the UI. What's the problem with that? The View, that should be dumb will need to hold some data to be presented, since it doesn't have any access to the Presenter.

The main problems with VIP are that it always must pass through the complete flow even if there are no real use cases to be executed, or if it's only a change of screen, increasing the complexity of a scene. If for instance you need to implement the UITableView protocols, all of their implementations and data must be kept inside the ViewController(or maybe an auxiliary class).

Interface naming is also important, and according to the SOLID principles, the Interactor shouldn't know anything about what's happening in the UI, making interface method names like viewDidLoad , didSelectCell and fetchView useless. Also, calling the Interactor by methods relying on business rules like fetchSomething isn't the best choice since the UI should not know any business rules as well. An intermediate layer between the UI itself and the business rules is really essential.

We need a new solution that makes your View content structure reactive, relying the View content in a separated layer and giving the choice to trigger some use case only if necessary.

VIP-ViewModel

This clean architecture comes as solution to the main issues within VIPER and VIP separating all the ViewModel content that is accessed from the ViewController in a separated layer, which listens to all the user interactions, decides when to change context(accesses a Coordinator), and triggers a use case when necessary, passing then through the VIP flow.

To achieve that, we have taken the VIP cycle, but this time, instead of the ViewController to be inside it, we create a new(or old) layer called ViewModel. The ViewModel basically listens to all the user interactions and ViewController lifecycle events handling them in an appropriated way according to the scene and provides all the content interface to update the View components:

What happens in this image? We basically have View and ViewModel linked to each other. ViewModel provides all the data for displaying in the View through an interface, listens to all necessary View events(viewDidLoad, button clicking and table view delegate events, etc) and asks the View for a layout update. This way we have a reactive interface between View-ViewController-ViewModel(you can even think of that as one View layer). At the same time, when there is some business rule to be executed, ViewModel asks Interactor for that, with the proper name, beginning the cycle. When some output is achieved by the Interactor, it sends the models to the Presenter, which just maps that into some display data and sends to the ViewModel. Then, this data can be accessed by the View when needed. When we need a change of screen that doesn't concern on any use case, we can change screens delegating the task to the Coordinator without passing through the VIP cycle.

Next, let's illustrate how the ViewModel should work with some code:

ViewModel tasks

  1. Listen to ViewController events(Usually the names of the functions are the same as the ViewController)
  2. Provide data to the ViewController as a ViewModel in the MVVM and the Presenter in VIPER
  3. Delegate a change of context to the Coordinator
  4. Delegate an update to the UI
  5. Delegate a use case to the Interactor(Passing through the VIP cycle)
The View accesses the ViewModel through an input protocol, which holds some content and implements its events
Receives parsed outputs from the Presenter(they are hold by the ViewModel to be accessed by the View)
It has a private display model that is filled when Presenter returns the parsed output from the use case. The interface variables compute data from this model. When the model is updated, the View should at the same way
How the View sees the ViewModel

What about the View? How does it communicate with the ViewModel?

Well, as we said before, the View sends its events through a ViewModelInputprotocol. Since it is an interface that allows the View to access all the necessary content to display, there is no need for a View contract holding multiple update methods. One method made to tell the View to update is enough and its implementation may consist in the View checking all the content in the ViewModel to fill:

The View accesses all the UI properties in the ViewModel and fills the interface

In the Displaying implementation above we can see that the View layout can be updated with the loading information, showing an activity in the front if it is in fact loading, it shows an error alert if there is some in the ViewModel and it reloads the tableView data. But be careful, you need to update objects like errorViewModel when the state changes to avoid side-effects.

This way, we make a redesign within a single function

Also, we can implement the tableView protocols in the ViewModel:

Since the implementation is kept inside the ViewModel concrete type, you need to make a cast to it with the contract hold in the View

Need to change screens and don't need any data from the Interactor? Just follow:

It's not necessary at all to pass through the VIP cycle for a change of screen.

Conclusion

Let's finish this article by highlighting the main advantages we are taking from this new architecture and some other points we need to review deeper:

  1. SOLID divides even more the scene responsabilities by taking some tasks from the ViewController and Presenter to keep in a new layer(ViewModel)
  2. More testability since we are now keeping the View content in a separated layer.
  3. Simplified flow. Now we don't have to iterate through the VIP cycle if we don't need a use case but just some presentation logic execution.
  4. More reactivity between View and presentation layers, just like MVVM and VIPER
  5. Just one method for updating the View with the parametrized data in the ViewModel.
  6. A linear flow for changing context.

When should you use this architecture?

If you have an app with simple business rules that doesn't actually need a reactive screen and not suitable to major bugs in a complex flow, this architecture may be a bee killing with a cannon. But if you are thinking of reactive screens with many complex business rules to be maintained in the long term, you should really consider the VIP-VM to attend your projects.

I really hope you have taken some knowledge from this article and hope you can merge all the benefits from the two major clean architectures into this one. Any comments are more then welcome, and hope enjoyed it! ;)

iOS developer- WWDC19 scholarship winner- Blockchain enthusiast

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Swift Text-To-Speech As Deep As Possible

SwiftUI: Things Change

Response to Cosmostation iOS app Mnemonic Phrase Import Issue

Snippets in Xcode

Swift 101 — Basic Property

Google apps will stop certain tracking to avoid the iOS “Allow Tracking” prompt

Required Initializers in swift

SwiftUI Dismiss Keyboard on outside tap

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Pedro Alvarez

Pedro Alvarez

iOS developer- WWDC19 scholarship winner- Blockchain enthusiast

More from Medium

UIView Lifecycle

How to organize mock files with SPM

Strategy Pattern in Swift

Clean architectures: Inner and Outer-Scene layers