iOS VIPER: The most suitable design patterns that come together

Image from https://br.pinterest.com/pin/477170523031160997/feedback/?invite_code=e6923e0973a249f1904d97d2dfade593&sender_id=348888439791431720

Resuming our iOS VIPER series, it's time to talk about the most important design patterns that fit a VIPER scene. Since VIPER is a complex architecture, there are multiple ways to create and bind its layers, establish a relationship between different sides and make your code more legible. I might be forgetting some other patterns that could be used, if I am, please paste a comment with some new suggestions. Said that, let's start.

Factory

As you may already know, Factory is a design pattern that is applied to customize creation of a huge structure of related objects. Basically, instead of initializing multiple objects and injecting them into each other, which may cause a lot messy and confusion, the Factory pattern delegates all this responsability to an extra type.

Creating and binding VIPER layers step by step

As you can see, our SceneFactory consists of a single method, which shall receive or not some inputs from our previous scene and creates a new one layer by layer by instantiating, injecting properties and assigning delegates. As a result, a whole new view controller is returned to be pushed into our screen stack.

But why it's the view controller who's being returned? Could it be Presenter our Interactor?

Since UI is the core of our app, it's very important to return an object that is related to our navigation and transition between our scenes, that are view controller types under the hood.

Of course you could return something else like Router by just establishing an interface between their view controllers and then triggering a change of context, but since this operation is pure UI, returning the view controller itself is cleaner.

Adapter

Adapter was designed to convert a data model into another type by injecting this input instance via init. Basically we apply that when we want to convert our Entities, which originate from the Interactor use cases into ViewModels that shall be consumed by our Presenter . You should remember Interactor and its entities are only related to business logic, no UI at all. These entities are adapted to some UI friendly format, ready to fill the interface. We shall declare an initialization method receiving our input type, ContactEntity and consume this data to create our new output object

Observer

Observer is a design pattern where we have a class that shall emit some events, which we call Observable and these events are handled by one or more Observers . The most standard way of implementing that is via NotificationCenters but the idea of that is to broadcast these events to multiple scenes in the app. Let's say the user turned a flag on in a scene and that should reflect some behavior or layout of a previous scene that is too complex to pass the information backwards. The most suitable solution for that is creating an Observable-Observer relationship between those scenes. As these events come from use cases, the special layer for keeping this relationship is the Interactor :

After the Observer Interactor receives an event, that may reflect changes in its presentation layer.

Facade

In the VIPER structure itself we can see Presenter as some kind of Facade since it works like a hub to all the other layers. It handles inputs from View and use case outputs from Interactor and decides which operation should go next: a screen update, some new use case or maybe a change of scene. The point of Facade is to abstract some logic and that's the idea of Presenter in our given architecture.

Delegation

The delegation pattern defines a protocol with some methods to be implemented by some class as a continuation to the pipeline of an origin class. We see that a lot in UIKit components(UITableView, UISearchBar, UITextField, etc). The shortest description of that is about handling events. We have delegates for our Presenter and Interactor layers, respectively some methods to tell the View some presentation logic was executed and to tell Presenter it received an output from some use case. Presenter implements a delegate of Interactor and View implements a delegate of Presenter . However, through my nomenclature I call the delegates from VIPER as outputs:

Implementing Delegate from Presenter
Implementing Delegate from Interactor

Dependency Injection

Dependency Injection is simply abstracting some part of the logic within another type and injecting this instance into the original class. That's exactly what we do when we split the scene's tasks into different layers, each upper layer abstracts some implementation and is injected into another layer.

Of course there are multiple ways of doing that, but in VIPER:

  • Interactor is injected into Presenter via init
  • Presenter is injected into View via init
  • View is injected into Presenter via assignment
  • Presenter is injected into Interactor via assignment
  • Router is injected into Presenter via init
  • View is injected into Router via assignment

For short, all the layers should be injected via init , except for the weak properties that should be injected via assignment. Check that in our Factory section for implementation details.

Dependency Inversion

This pattern comes together with Dependency Injection but works for testing purposes. As we aim isolation between different logics, when we don't want to rely on a logic of an injected layer, instead of injecting the concrete type itself, we inject a mock that implements the same protocol in order to only test the logic from the suit under test:

This way, if the Interactor implementation has some issues, they won't interfere in the expected result from the method we are testing from Presenter. Check our mocking class:

As it implements the ContactListInteractorInput protocol it perfectly fits Presenter when we want to test it.

Coordinator

I know it may be worthless talking about it again, but Coordinator is a design pattern where we abstract navigation logic from a scene into a single layer. In VIPER this is the same as Router . Of course there may be some differences from the traditional Coordinator that may work to implement navigation to an entire application and our Router should be only the exit point of a scene but the purpose is the same, just more restricted:

As I talked before, it's essential for a Router/Coordinator to have access to the main navigationController.

Conclusion

As we saw in this article, there are multiple design patterns that fit a VIPER project in a scene. Some of them like Facade, Coordinator and Dependency Injection are essential for VIPER to work properly. Maybe you already knew them but didn't identify by name. They are great solutions to make your code cleaner, testable and legible. I hope you enjoyed ;)

--

--

iOS developer- WWDC19 scholarship winner- Blockchain enthusiast

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