Battle of the iOS Architecture Patterns: Model View Presenter (MVP)

Radu Dan
Geek Culture
Published in
8 min readMay 17, 2021
Architecture Series — Model View Presenter (MVP)

Motivation

Before starting to develop an iOS app, we have to think of the structure of the project. We need to consider how we add those pieces of code together so they make sense later on — when we come back and revisit a part of the app — and how to form a known “language” with the other developers.

In this third article of the series, we will transform the MVVM app in MVP.
As usual, we will see how we apply the pattern to each screen, seeing the actual implementation and the source code. At the end, we will show the build times and detail some key observations about MVP, compared to the other architecture patterns.

If you just want to see the code, feel free to skip this post. The code is available open source on GitHub.

Why an Architecture Pattern for Your iOS App?

The most important thing to consider is to have an app that can be maintainable. You know the view goes there, that this view controller should do X and not Y. And more important, others know that too.

Here are some advantages of choosing a good architecture pattern:

  • Easier to maintain
  • Easier to test the business logic
  • Develop a common language with the other teammates
  • Separate the responsibility of your entities
  • Fewer bugs

Defining the Requirements

Given an iOS application with six or seven screens, we are going to develop it using the most popular architecture patterns from the iOS world: MVC, MVVM, MVP, VIPER, VIP, and Coordinators.

The demo app is called Football Gather and is a simple way for friends to track the scores of their amateur football matches.

Main features

Ability to:

  • Add players in the app
  • Assign teams to the players
  • Edit players
  • Set a countdown timer for matches

Screen mockups

Screen mockups of “Football Gather”, the iOS app

Backend

The app is powered by a web app developed in the Vapor web framework. You can check out the app in my Vapor 3 initial article and the article about Migrating to Vapor 4.

What is MVP

MVP is slightly the same as MVVM, but with some key notes:

  • You now have a presenter layer.
  • You can control the view from the presenter layer.

Model

  • The Model layer is exactly as in the others, it is used to encapsulate business data.
  • Is an interface responsible for domain data.

Communication

  • When something happens in the view layer, for example when the user initiates an action, it is communicated to the model through the Presenter.
  • When the model is changed, for example when new data is made available and we need to update the UI, the Presenter updates the View.

View

  • View layer is the same as in MVVM, but the View now lacks responsibility for updating its state. The presenter owns the View..

Communication

  • Views can’t communicate directly with the Model, everything is done through the Presenter.

Presenter

  • Responsible for handling the events coming from the view and triggering the appropriate events with the Model.
  • Connects the View with the Model, but without any logic added to the View.
  • Has a 1:1 mapping to a View.

Communication

  • Can communicate with both layers, Model and View/View Controller.
  • The view updates will be done from the Presenter.
  • When data changes, it makes sure those changes are communicated to the user interface, updating the View.

When to use MVP

Use it when you feel MVC and MVVM doesn’t work quite well for your use cases. You want to make your app more modularised and increase your code coverage.
Don’t use it when you are a beginner or you don’t have that much experience into iOS development.
Be prepared to write more code.

For our app, we separated the View layer into two components: ViewController and the actual View.
The ViewController acts as a Coordinator / Router and holds a reference to the view, usually set as an IBOutlet

Advantages

  • The separation of layers is better than in the other patterns.
  • We can test most of the business logic.

Disadvantages

  • The assembly problem is revealed more prominently in MVP. Most likely you will have to introduce a Router or a Coordinator to take care of navigation and module assembly.
  • There is a risk that your Presenter will turn into a massive class, because he has more responsibilities.

Applying to our code

There are two big steps we need to do:

  1. Go one by one through the ViewModels and turn them into Presenters
  2. Separate the View from the ViewController

The applied MVP pattern is detailed below:

LoginPresenter

Let’s see how the LoginPresenter looks like:

All parameters will be injected through the initialiser.

The Keychain interactions are defined below:

And we have the two services:

The register function is basically the same as the login one:

The LoginView has the following protocols:

Most of the ViewController logic is now inside the View.

Finally, the ViewController:

We take screen by screen, and turn the existing MVVM architecture into an MVP.

PlayerListPresenter

Next screen is PlayerList, and we start with the web API calls:

Now, the check for playe deletion is made inside the Presenter and not in the View/ViewController.

If we look in the PlayerListView, at table view's data source methods, we observe that the Presenter is behaving exactly as a ViewModel:

The PlayerListViewController acts now more as a router between Edit, Confirm and Add screens.

Breaking into responsibilities, PlayerList module has the following components.

PlayerListViewController responsibilities:

  • Is implementing the PlayerListTogglable protocol to go back to the listView mode state, whenever a gather is completed (called from GatherViewController).
  • Holds an IBOutlet to PlayerListView.
  • It sets the presenter, view delegate and tells the view to setup.
  • Handles the navigation logic and constructs that models for Edit, Add and Confirm screens.
  • Implements the PlayerListViewDelegate and does the following operations:
  • Changes the title when the view requested to change it (func didRequestToChangeTitle(_ title: String).
  • Adds the right navigation bar button item (Select or Cancel selection of players)
  • Performs the appropriate segue with the identifier constructed in the Presenter.
  • Presents a simple alert with a title and a message (when the service failed, for example)
  • Presents the delete confirmation alert.
  • By implementing the PlayerDetailViewControllerDelegate, when a player is edited, it tells the View to refresh.
  • Same case for AddPlayerDelegate, and here, it tells the View to load again the list of players.

PlayerListView responsibilities:

  • Exposes the public API, PlayerListViewProtocol. This layer should be as dumb as possible and not do complex things.

PlayerListPresenter responsibilities:

  • At a first glimpse at PlayerListPresenterProtocol, we notice it does a lot of things.
  • Exposes the needed methods for the View, such as barButtonItemTitle, barButtonItemIsEnabled, and so on.

PlayerListViewState responsibilities:

  • We decided to extract the ViewState into a new file, keeping the same functionality that we had in MVVM, using the Factory Method pattern to allocate the different states of PlayerListView.

PlayerDetail screen

Continuing with PlayerDetail screen, we separate the View from the ViewController.

Following the same pattern, the navigation to edit screen is done through delegation:

  • user taps one of the row that corresponds with a player property. The View tells the ViewController that we want to edit that field, the ViewController performs the correct segue. In prepare:for:segue method, we allocate the properties required to edit the player.

Inside PlayerDetailViewController:

PlayerDetailView is presented below:

And the table view delegate and data source implementation:

The PlayerDetailPresenter:

Edit Screen

We follow the same approach for the remaining screens of the app.
Exemplifying below the PlayerEdit functionality. The PlayerEditView class is basically the new ViewController.

The selectors are pretty straightforward:

And the Public API:

Finally, the UITableViewDataSource and UITableViewDelegate methods:

PlayerEditPresenter handles the business logic and exposes the properties for updating the UI elements.

An API call is detailed below:

PlayerAdd, Confirm and Gather screens follow the same approach.

Testing our business logic

The testing approach is 90% the same as we did for MVVM.

In addition, we need to mock the view and check if the appropriate methods were called. For example, when a service API call is made, check if the view reloaded its state or handled the error in case of failures.

Unit Testing below GatherPresenter:

Testing the countdownTimerLabelText:

Toggle timer becomes more interesting:

And below is the mock view:

I’d say that testing the presenter is very cool. You don’t need to do magic stuff, and the methods are very small in size which is helping.
The complex thing comes with the fact that you will need to mock the View layer and check if some parameters are changing accordingly.

Key Metrics

Lines of code — View Controllers

Lines of code — Views

Lines of code — Presenters

Lines of code — Local Models

Unit Tests

Build Times

Tests were run in iPhone 8 Simulator, with iOS 14.3, using Xcode 12.4 and on an i9 MacBook Pro 2019.

Conclusion

The application has now been rewritten from MVVM to MVP. The approach was straightforward, we took ViewModel by ViewModel and replaced it with a Presenter layer.

Additionally, we created a new layer — View — out of the ViewController to better separate the concerns. The code looks cleaner, view controllers are thinner, the classes and functions are small and concentrate doing one thing, embracing the Single Responsibility Principle.

I personally prefer this pattern rather than MVVM when it comes to apps developed with UIKit in mind. It feels more natural than MVVM.

Taking a look at the key metrics we can make the following notes:

  • the View Controllers are much thinner, overall we reduced their size with more than 1,000 lines of code
  • however, we introduced a new layer for the UI updates — called the View
  • Presenters are bigger than View Models, because they hold an additional responsibility, to manage views
  • writing unit tests was similar as per MVVM, obtaining almost the same code coverage, 97.2%
  • having more files and classes, we had a small impact on the build time, being increased with 530 ms compared with MVVM and 400 ms from MVC
  • surprisingly, the average unit test execution time has been quicker with 1,36 seconds compared with MVVM
  • comparing to the MVC pattern, the unit tests covering the business logic were much easier to write

Really cool we saw together how to transform an app written in MVVM into a different pattern, such as MVP. From my point of view, MVP with the separation of View from the ViewController, is much nicer than MVVM. It brings more power to your layers, making them decoupled from each other and is much easier to use dependency injection.

Thanks for staying until the end! As usual, here are some useful links below.

Useful Links

--

--