My name is Sonam, I am a senior iOS engineer at ustwo. ustwo is the company that made Monument Valley. We have four different studios in Sweden, London, Australia, and New York. I am based in New York. I work on client work, along with our own internal experiments. We do apps websites, VR and AR work in the New York studio.
Today, I’m going to talk about the Viper design pattern, and show an implementation of the design pattern itself. Viper is not commonly used, but it is becoming more often talked about.
Some of the patterns we use are MVVM, MVC, and Viper (view interactor presenter entity and router), which leaves a lot of people either intimidated, confused or scared.
Viper was created at Mutual Mobile, an agency in Austin, Texas. They came up with a design pattern building off of the clean architecture, Uncle Bob’s Clean Architecture. After using and publishing it, it became more common. There’s no exact standard for Viper, which is why you often see different code snippets and examples online.
Module Based Viper Standard
Viper is module based; whenever you build your application, you want to think about a user’s story and build a module around it.
Get more development news like this
Suppose you want to play a video in your application, and you have the following actions: play, pause, and scrub. You want to make a module around those actions and broken up into these objects: your view, interactor, presenter, entity, and router.
When making a Viper module, the interactor will be the one interacting with entities and data managers. The entity should not be coordinated objects and should be as simple as possible, such as an enum or simple object to represent the date for the particular user story. In Viper, everything is a protocol.
When you’re building Viper with a framework, the router seemingly does not make sense. A router is an object in charge of navigating your application logic. Think about it as holding your navigation cone controller and holding all the flows that will exist through your app, and pushing one view controller to another view controller. I don’t think it’s necessary.
In Viper the inputs and outputs are important, and it revolves around actions. When I tap something on the view, the view will then talk to the presenter, the presenter will then talk to the interactor, it then travels downstream, and comes back upstream.
I want to focus on the input and outputs of these relationships.
Inputs and outputs
The interactor output protocol, you would think that this applies to the interactor, but it applies to the presenter. The presenter conforms to this protocol, and it’s called the interactor output. When a view sends a message to the presenter, the presenter outputs to the interactor.
When building a module for other developers to use it in their code, I took away the idea of a router because I want the application and the developers who are in charge of the application to be responsible for the navigation logic.
As a result, I introduced the idea of a builder with a contract. A contract will define all the protocols that are used within your module. It can go to place for any developer to understand what’s happening in your entire module, and what is needed to satisfy a user story.
The builder is what will build your module. You can think of it as a configurator. In my example, I have the ViewController asking the builder to build the Viper module
A Sample Demo Framework: Video Playback Kit
A sample framework that I built and few of us have been working on, is called the Video Playback Kit. It’s open source.
The purpose of the video playback kit was to test Viper as an architecture. Video playback has to deal with the player state, UI updates, and then data synchronization.
The builder has a function to build the view in the cell, with the entity type for specifying the type of video.
Next in the Viper module is the contract. You can create a protocol outlying it as the use case that your Viper module will support.
You have a presenter that your view is attached to, but the presenter is also attached to the view. When you set up all the dependencies and creating the Viper module, you have to pay attention that each object all have to connect to each other, which can lead to retained cycles. Make sure you’re making your variables weak when it’s necessary.
The presenter in the Viper architecture is supposed to format the UI. It should not import UI kit, and it should not know anything UIKit related, but it should setup the view for everything the view needs.
What are the benefits of this? Viper may seem like overkill. Each of your objects is doing one thing, and each of these classes is easier to test than it would be with MVVM or MVC. You can tell that every method, every function, is doing one thing. In some cases, it’s passing information along.
Viper is protocol-oriented, which means that you can create your own version of any module as long as you conform to the protocols laid out in the contract. It’s much easier to test than MVC and MVVM. It adheres to the single responsibility principle; when you put it into place, it makes your code cleaner. Lastly, it’s reusable.
There are many projects that will generate an entire Viper module with all the boilerplate code for you. I do not recommend using those tools - it’s good to make it yourself - you will often modify it, and that’s okay.
If you are agile, or your team iterates every day or has releases every week, I don’t think Viper is a good pattern to use. On the other hand, if you are working on a reusable component that you know the features and the acceptance criteria, and it will be reused many times within your application, I recommend Viper.
To note, Viper is still not a common design pattern in the iOS developer world. If you plan on sharing your code with other team members, or maybe you work at an agency and you have to hand off your code to another team within a couple months or a year, it is not ideal.
- Sonam’s blog post on the subject with even more detail, code samples, and diagrams.
- Video Playback Kit on GitHub.
About the content
This talk was delivered live in September 2017 at try! Swift NYC. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.