Introduction
For a long time, I experimented and worked with the VIPER architecture and wrote blog articles about the pros and cons based on my experience in production applications.
VIPER is great, with many benefits: it follows the Clean Architecture principles, provides you with a codebase that is future proof for business requirements updates or new features to add, allows you to work with many engineers on the same codebase without conflicting between one another, etc.
Although, when I started a new project at Prolific Interactive, I was looking for alternatives to see if a different architecture could provide me with a better flow. Indeed, the biggest pain point in my opinion with VIPER is the Presenter. It sits in the center of all your other VIPER layers, and its role is not very clear. For example, the Presenter can:
- Get called by the view after a user interaction
- Ask the Router to navigate somewhere
- Tell the Interactor to provide data
- Format business data coming from the Interactor to something understandable for the View
As you can see, this breaks the Single Responsibility Principle. The Presenter does a lot, and even worse, information can come from both the View and the Interactor. It makes it harder to track where the data is coming from, where it is going, especially when you’re trying to debug. I remember spending quite some time tracking data issues by putting breakpoints and jumping around functions in order to find where my bug was coming from.
View-Interactor-Presenter Benefits
I was looking for an architecture that could address these issues and at the same time keep the great benefits that I enjoyed with VIPER.
And then I came across Clean Swift.
I will not go too much into details here since Raymond Law, the author of the architecture who created this website, does a great job describing how VIP works and how to organize your code using it. I highly recommend you subscribing to his newsletters, they provide updates on the architecture, tips on how to identify issues in your codebase and how using VIP can solve them.
Control The Data Flow
My favorite feature from the VIP architecture is the data flow. In VIPER, data crosses the layers from the View to the Interactor and even further if you add layers handling API calls or database management. It becomes difficult to track data issues across a VIPER stack.
Whith VIP, the flow is circular and unidirectional between the View, Interactor and Presenter. You don’t have data going back and forth and it makes life much easier to understand how things are floating and track any issues along the way.
Clean Abstraction
Each VIP layer is based on a protocol that helps you keep abstraction. For example, it means that the View only has a reference of an Interactor protocol and not a concrete implementation of it. You can then easily swap things around, which becomes extremely useful for unit testing behaviors in your feature with mock layers.
Another great benefit is you can swap layers if you want to build different apps with a similar behavior. You can swap the View layer for a custom UI, but keep the Interactor the same since the business logic works the same way — think about iPhone vs iPad view.
Finally, VIP keeps dependencies clean and controlled. Low level business logic layers do not have access to high level UI logic layers, which means when a change needs to happen, only the layers that need to be updated are, and nothing else. More often, your UI will change, but your low level business logic — think about entities or API calls / JSON parsing — will not change that much. With VIP, every change happens only where it’s necessary, so you keep your updates small and focused.
Similarities with VIPER
If you used VIPER in the past, VIP should look very familiar. You find the same layers in VIP — including Router and Data Managers — but organized in a different way. VIPER and VIP share the same benefits in terms of Clean Architecture or SOLID principles. Chosing between the two will be a matter of preferences, although given my experience with both of them, I would most likely opt for VIP next time since you have less code to write due to the better defined Presenter’s role.
Testing
By implementing protocols behind each VIP layer, it becomes trivial to mock them by conforming to the same protocol, and make sure functions get called when they should. You can test that your UI was created properly by testing the View. Or you can focus your tests on the business logic by isolating your Interactor in a test environment and make sure your mock data gets handled as expected.
Other Design Patterns To Consider With VIP
VIP by itself is a great architecture, but there are some design patterns you can complement your codebase with that will make your architecture even more powerful and easy to use.
Builder
Typically, the Router layer is responsible for creating all the different layers and dependencies associated with a VIP stack before navigating to it. Unfortunately, creating a VIP stack can be sometimes tedious, especially if you have a lot of dependencies to inject. And if you use a VIP stack in multiple places in your application, repeating creation code isn’t something you want to do.
In order to isolate this logic and make it consistent and reusable, I recommend using builders to create all the layers you need behind a reusable and easy-to-use interface. You will move all the construction logic into a centralized class, and will simplify the work of other developers to create the VIP stack you worked on. On top of that, you can ensure that the VIP stack will be created the same way every time, and no dependencies will be forgotten.
Using the builder design pattern was my recommendation for my article on VIPER, and it makes sense for VIP as well.
Abstract Factory
If you want to easily create a VIP layer dynamically, an abstract factory can be very useful. Since each layer is backed by a protocol, you can create a concrete implementation of it depending on your case.
Think about A/B testing a feature: in scenario A you will generate view A conforming to the protocol View, and in scenario B you will create view B conforming to the same protocol View.
This applies to mocks as well, by being able to generate mocks through a factory, it can become very convenient for your testing.
Façade
When dealing with complex systems in your application, it can be beneficial for your code to rely on a façade design pattern to simplify its access.
Most of the time, the interactor can be the main beneficiary of this pattern. When business data has to be accessed by the Interactor, and it doesn’t have a nice API for it, creating a façade will simplify code for your Interactor. It becomes especially useful when multiple Interactors have to access data from the same place (best example would be a local database).
A façade will make access to this data easy and concise for all your Interactors, and will keep data access logic centralized in the same layer.
Conclusion
I hope this article showed you the benefits of Clean Architecture through the interpretation of the View-Interactor-Presenter. There are many other ways of accomplishing the goals of Clean Architecture, but I feel that VIP did a great job of keeping everything simple and easy to use.
The community around VIP seems very active as well. I highly recommend reading through the Clean Swift website and subscribe to the newsletter. There are even books available for those who want to dive deeper into the subject and learn all of the different VIP facets.
My best advice is to practice this architecture and all the principles described in this article. Xcode Playgrounds are a fantastic way of testing new approaches and getting instant feedback from your code. Try to create a fake VIP stack. Try to change concrete implementations of your protocols with a factory. Make a builder and see how easy it is to create all the VIP layers. Come up with your own interpretation of Clean Architecture if you want to.
Ultimately, everything that can make your code cleaner and easier to maintain will make your application more successful.
Good luck!
You must be logged in to post a comment.