Understanding and implementing VIPER architecture for iOS applications -Part 1 : VIPER vs MVVM

Layers of the VIPER architecture

V.I.P.E.R is an acronym for five architectural portions of the application that communicate with each other, the name stands for:

  • View: Part of the architecture responsible
  • Interactor: Abstracts the interaction with the entities and notifies the presenter for updating the view
  • Presenter: Transforms data so that it can be displayed in the correct format by the view
  • Entity: The data model behind the application
  • Router: Manages the coordination and the movement between different views

Communication between layers

One note about the acronym is that it doesn’t contain information about which layers communicate with which other layer. For that reason is important to understand and establish beforehand the contract between the architecture units, to disallow them from being able to interact with every layer recklessly.

Differences with MVVM: Behavior

MVVM

While VIPER enforces a strict contract between components of the architecture by adding some in between layers of complexity, MVVM is an architectural pattern that is mainly about splitting the concerns of showing and handling the data. How MVVM is usually implemented is by letting the View store a reference to the ViewModel, with the latter being the component delegated to communicate with the underlying model of data by managing the binding.
In general, the principle of the MVVM architecture when dealing with UIKit can be described as such:

  1. ViewController informs the referenced ViewModel that it needs to fetch some data by calling a VM method
  2. ViewModel starts the process of data fetching using the Model for encapsulating and representing the data
  3. ViewModel updates its property at the end of the successful fetch AND implements both the binding and the conversion of the data for the view to be displayed.
  4. View inside the ViewController is informed and reacts to the change by displaying the correct updated data

VIPER

On the contrary, a typical flow represented by a VIPER architecture using UIKit can be written as follows:

  1. ViewController tells the Interactor to load/reload the data it needs by using the Presenter. ViewControllerdoes NOT know who the specific Interactor is and is in the dark about how the communication with it is executed(i.e: is not informed of the API contract).
  2. The Presenter instead, holds a reference to the Interactor and notifies it about the need of the view to receive updated data
  3. Interactor receives the message and triggers the new data fetch using the Entity for holding the data.
  4. On success, the Interactor advertises to the Presenter that data has been received and encapsulated correctly, and passes the data to it
  5. The Presenter then receives the data and executes the needed conversion and transformation on it in order to give the View the correct format for the informations to be displayed correctly

MVVM vs VIPER

To make the differences more clear, given that some of them can be very subtle, the following table summarizes the responsibilities of the architecture units and what they accomplish

Common pitfalls of MVVM

Pitfall 1: Abusing the ViewModel in SwiftUI

Regarding SwiftUI, there would be a lot to say of how MVVM is usually implemented. Given the different paradigm of development that SwiftUI allows, is very easy to make a design mess by letting different views bind and observe the same ViewModel and/or Presenter. On of the reasons for the fundamental misunderstandings is that what is commonly written as View in SwiftUI, in reality is actually a Model that conforms to View via the var body: some View computed property. Given the complexity and the delicacy, this topic will be skipped here (at least in this part), you can read more about it here.

Pitfall 2: Using ViewModel as a network request manager

Another common pitfall is that in a lot of projects (both in UIKit and SwiftUI) the ViewModel serves the only purpose of handling and managing the network request for the view itself, and it does so by directly calling URLSession or AFRequest leveraging Alamofire. This can easily lead to bad design when the size of the projects increases, because by definition a ViewModel is not a shared instance, and when dealing with complex REST APIs you’ll probably have to handle authentication, task concurrency, and task failure.

Is it wrong for the ViewModel to handle network requests?

Letting the ViewModel handle the logic of network requests can sometimes be fine, especially in smaller iOS application. But as the number of views/viewcontrollers grows you’ll find yourself either having different ViewModels that end up doing the same thing, duplicating code and forcing yourself to maintain the network calls in two different parts of the codebase, or the same ViewModel that gets passed around views over and over again (see pitfall 3).

Pitfall 3: Sharing ViewModel between many Views

How many times you were working on a View in SwiftUI triggered by a NavigationLink or Modal display and were trying to display rows of data only to realize you need part of the ViewModel (calls/entities) that was being used in the previous View?
If you ever had this problem you have probably solved it by either passing an @EnvironmentObject down the hierarchy letting the whole hierarchy now aware of that ViewModel or you passed it trough a direct binding in the initialization parameters.

So what is the problem of that?

ViewModels are reference types. This means that if you pass the same instance in different parts of your code you’ll always have the risk of having side effects on your object and compromise its integrity, because you no longer have the protection given by the immutability.

Part 1 — Conclusion

The next part of the series will deal with what a VIPER architecture tries to do to solve common pitfalls and problems of the MVVM approach and how it structures the communication between layers by enforcing a contract using Swift Protocols. Given that VIPER has its own flaws and pitfalls too (that can easily lead to over-engineering) we’ll be also analyzing when using VIPER makes sense and how to avoid writing boilerplate code.

--

--

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
Simone Giordano

Simone Giordano

iOS Engineer — Bachelor’s Degree in Software Engineering. Passion in F1 and video games.