Ponte Vecchio (FI) — Picture taken walking to #PragmaConf15 Venue in Florence — XT1, 23mm
For those of us out there since the first iOS SDK was released we have seen a big shift in application engineering. While in the past we were all quite happy with what today we joke as Massive View Controller, now we face problems that require more structured engineering.
Irrespective of the technology decisions, what we should all try to achieve in a single word is: simplicity. Simplicity means that our codebase, which can be quite large, should be simple to communicate and maintain. Should also be simple to reuse, and should be simple to test, not just for the sake of code coverage, but for the confidence that as engineers we build bridges that won’t collapse under our feet.
In this post I will take you through a structured approach to mobile application engineering to achieve the simplicity mentioned above. This is not necessarily a new or “one fits all” way of doing things, but something that we have seen working quite well where I work at MTT, creating great apps for airlines and travel companies worldwide.
We will together build on top of the VIPER Architecture in order to achieve a high order modularity by formalising the building blocks of the design, focusing on reuse and customization with an high degree of testability.
What we are going to discuss here is tailored to iOS but can be successfully applied to Android and most likely other platforms.
If you have ever built a complex application you know already that most of the time MVC pattern falls short, as it leads too easily to (let’s abuse of this joke once again) Massive View Controllers; and you are already aware of some other interesting strategies on the subject tending towards something that helps us creating less Massive but more Lighter View Controllers.
Without a properly designed and communicated approach, it is quite easy to fall back to the “massive something” world.
An effective and clearly communicated blueprint is strategic in the engineering business.
I will not dig into technology choices or chunks of code here, but instead I will focus on the blueprint and the relevant parts to successfully apply the design.
Starting from VIPER Architecture
The main idea of VIPER is to create a (mostly vertically) layered architecture where the Single Responsibility Principle is achieved by using several different components.
Here is a standard representation of the different VIPER components and relationships between them:
The acronym stands for:
View: encapsulates the actual views, starting from view controllers
Interactor: encapsulates all the business logic and datasource access
Presenter: encapsulates the presentation logic
Entity: the object model manipulated by the Interactor
Routing: or Wireframe encapsulates the navigation logic
We want to go deeper in the VIPER Module definition and make it the building block of the (B)VIPER approach.
Toward a Standard Module
While the VIPER layering allows for great decoupling, the way Modules should be built and communicate is left to the designer.
We can think of a Module as a unit of reusability that encapsulates a Use Case, or a Functionality, or one or more Stories. To start simple, just think of a Module as a single iPhone Screen; we can build from there.
Let’s start sketching with a 3 screens example Application: from an initial category selection to a list of items to a detailed view of the item.
Step 1 — Module as a Black Box
Example Application made of 3 Modules linked to each other.
On the iOS platform the foundational building block of an iOS App is the UIViewController. The navigation happens from UIViewController to UIViewController.
It is foolish to fight against the framework, so a Module should be presenting itself to the outside world as a standard UIViewController.
From an external point of view each Module is a standard UIViewController.
This allows the use of a Module into any iOS Application without any additional knowledge by the hosting application: a Module can be navigated from and navigate to something that has nothing to do with the current design.
Step 2 — The Wireframe role
When investigating the navigation between Modules, I want to stress that the Wireframe only business is to define the navigation to the next Module. Other approaches leave the business of Module creation to the Wireframe itself which violates the Single Responsibility Principle.
The Wireframe’s only business is to navigate to the next Module(s).
This of course implies some sort of knowledge of the next Module.
Step 3 — The Module Builder
To better decouple Modules to each other and achieve a strong self containment, we introduce the Module Builder component to the standard VIPER set of components.
The Module Builder is in charge of creating all the components of the Module (Presenter, View, Interactor, …) and make the connections. The Module Builder protocol usually presents a single methods like this:
It is of course possible to have the UIViewController implement a protocol representing the Module external interface if needed.
The Standard (B)VIPER Module
This is a visual representation of a Standard (B)VIPER Module.
- Module as black box for the outside world, presenting itself as a standard UIViewController. This allows an easy re-use of a Module even within apps that have a complete different design.
- Design by Interface as default approach where each component exposes a contract. It allows an easy swap of concrete implementations thus behaviours, plus easy mocking for testing.
- Builder is the key for reusability, declares what data is needed for the Module to be built, inject all the needed components letting the implementations change easily. A Builder must know the next Module(s) Builder.
- Services as data sources. A Service provides a contract to the Interactor to consume in order to retrieve or create Entities. Services are horizontal to Modules avoiding code duplication. Examples are Weather Service or Stock Service; those are not part of any Module. Each Service should ideally live in its own Library.
The relationship between Presenter and View
From the standard VIPER definition:
“The Presenter can only prepare the data for display in the View. […] The View is passive. It waits for the Presenter to give it content to display; it never asks the Presenter for data.” Introduction to VIPER
Let’s try to map this into some more canonical terms.
VIPER promotes MVP pattern where the Presenter handles View interactions, delegating any business logic to the Interactor if needed (so Presenter cannot modify the Model ideally) then asking the View to update by preparing “data for display”.
We need to introduce the concept of the Presentation Model which is our “data for display”. Ideally this model is UIKit agnostic as it exposes pure ready to display data in the form of String or Numbers: you can hypothetically share this code with other platforms with this approach.
“Presentation Model is of a fully self-contained class that represents all the data and behaviour of the UI […] Presentation Model will have data fields for all the dynamic information of the view. This won’t just include the contents of controls, but also things like whether or not they are enabled.” Martin Fowler Presentation Model.
I’ve intentionally omitted any form of “logic” within the Presentation Model because we want to achieve the following:
- The View informs the Presenter on user interactions
- The Presenter creates an immutable Presentation Model from Domain Model provided by the Interactor
- The Presenter provides the View with the Presentation Model
Here is the visual representation
The Presentation Model Builder is extracted from the Presenter logic as it may get quite complicated when multiple Domain Models are needed to build a single Presentation Model. It also makes a lot more sense in terms of Single Responsibility.
A reactive programming style could be achieved here as well.
Dependency Injection for Builders
The Builder is in charge of building the Module it ships with. The best way of doing it is via dependency injection. Typhoon is probably the best DI Library for iOS available today.
Without getting into too much detail here, the DI gives you the ability to describe declaratively what Concrete Implementation for each Protocol is needed and the scope.
It is pretty much like playing LEGO® with the components building the Module. By injecting a custom component, changing the behaviour or the Visual Representation of the Module is easy.
Testing the Module
If you got this far you know that the main goal is to make testing easy. We can write isolation checks for each component of the Module easily by mocking the dependencies.
Adopting the Presentation Model approach we also get a great level of confidence on what the User Interface is actually going to display, even without touching a single UIKit class.
If you believe that your tests should actually cover the integration of your codebase from Network to User Interface, as this is what your user is actually going to experience, then you are not limited and I believe it is a nice addition on top of it.
Such architecture would be rather ridiculous without a proper way to manage dependencies like CocoaPods.
As Modules are building blocks of Applications each Module should ideally live in its own Library, allowing the creation of Applications made of several Modules/Libraries. We are now playing LEGO® with different pieces: Modules rather than components within the Modules.
We started from the word “simplicity”. On a first glance this may not seem exactly simple, as we added extra component to the canonic VIPER design. As the blueprint should be quite clear at this stage it is up to you to decide if the number of components per Module is too high for your specific business. Maybe your business is better tailored for a MVVM design or even a plain MVC.
Here at MTT we have the sort of challenges that makes the (B)VIPER architecture very useful, and the experience of presenting and applying such approach to teams usually goes from this on day 1:
To this on day 3 or 4:
Some of the best feedback I have received so far is:
“Now that I am familiar with it, I can jump through unknown Modules very quickly and I always know where to put my code” and
“I can finally write tests easily and quickly, which it has always been a nightmare for me to do.”
Final thanks to the guys at #PragmaConf15 Florence. It was a real surprise, kudos to all the organisers and great speakers. There I got the chance to talk with great guys at Artsy (@artsy) and FanDuel and many others, that encouraged me to write about this structured approach on engineering big apps.