The use and adaptation of the VIPER architecture on our own example of development, part 1
In this article, we want to share with you our experience of using and adapting the architecture of the Viper by describing our own example of development. And today we're going to discuss the basics…
Many developers face an issue when code becomes hard to read, debug and fix bugs. Classes become huge together with adding features and development turns to infinite “bugfixing”. That’s why it’s important to choose proper software architecture before developing an application, otherwise, it will consume much time to maintain and improve. As a result, it will help to save time and efforts, so proper architecture is the first step to success!
Quality architecture should meet the following requirements:
- The effectiveness of performing the assigned tasks. If designed architecture performs the tasks poorly and doesn't fit into the requirements of functionality, stability, performance, and development time, it can hardly be called a good one.
- Well readable and understandable code. First and foremost, a code should be clear and properly structured.
- The scalability and flexibility of the system. A good architecture allows you to painlessly add new entities and functions, as well as make changes to existing functionality.
However, there is no reason to reinvent the wheel and spend a lot of time developing a completely unique architectural pattern. It's much easier to use the existing one.
There’re several most famous architectures named MVC, MVP, MVVM, VIPER. Let’s probe deep into each of them.
MVC (Model View Controller)
MVC is the most simple architecture Apple recommends.
The basic idea of MVC architecture is to split up all the elements (fragments) of the code into three separate units: the Model (domain data or a data access layer), the View (user interface) and the Controller (interface between Model and View). MVC allows each block to be controlled independently of the two others, and it greatly simplifies and systematizes the development process.
MVC implies that the Model manipulates data and provides it to the Controller that renders the View. And the View remains stateless just displaying data of the Model to a user.
However, the described model may not work perfectly in all conditions. And in reality, the View and the Controller often become a single unit looking like this:
So, ViewController transforms into the Massive View Controller forced to handle almost all the processes itself. And the main task for developers, in this case, is to offload it.
MVP (Model View Presenter)
It turned out that in some cases MVC architecture is not working as effectively as one expects (for the reasons described above). So there was MVP pattern designed, having become a bit improved version of MVC.
The difference between MVC and MVP is the Presenter acting as the mediator between the View (actually ViewController) and the Model. The Presenter significantly unloads the ViewController by moving business logic to the Presenter, so the ViewController is only responsible for the layout and its lifecycle.
This architecture makes it easier to mock the View, the Presenter and the Model that leads to better testability of the code which is an obvious advantage of MVP.
MVVM (Model View ViewModel)
Another popular pattern is MVVM (Model View ViewModel). It looks similar to MVP architecture, but instead Presenter it has ViewModel between the View (view controller) and the Model (data).
The important difference of this model from the MVP is that ViewModel doesn’t refer to View, it works directly with model updating it with user interactions from View and receives updates from Model updating only itself (not View). View can receive updates using observer if needed. So ViewModel prepares the data for View that unloads controller.
The last architecture we will talk about is VIPER. It’s a very effective architecture pattern, in fact, the first one that cares about navigation (routing) between modules.
It consists of 5 parts: View, Presenter, Interactor, Entity, Router.
- The view is the ViewController. The View’s waiting for the Presenter to transmit the content and display it to a user; it never requests the data from the Presenter.
- Presenter prepares data for the View. It gathers data from user interactions and can send requests to the Interactor. The Presenter also gets results from the Interactor and convert the results to a state being most effective to display on the View.
- Interactor (a new unit) contains the business logic and networking for managing objects (Entity) to perform a specific task.
- Entity is the objects managed by the Interactor. So it’s actually a model.
- Router (another new unit) is responsible for segues between modules.
- Data Store (e.g., web service, database) provides the Entity to the Interactor. Applying its business logic, Interactor selects the Entity from the data store, manages it and then returns it in an updated form back to the datastore.
VIPER module is a user story that can be represented either as one screen or as a whole story. The good example is a registration flow consisting of several screens such as SignUp, Confirmation, etc.
Each module being an independent part requires an additional unit able to manage the entire process. This role is played by the Configurator which task is not only to create a module but also provide it with all necessary Services and information.
Improving and Simplifying VIPER at the same time
VIPER architecture was reliable and usable from the outset, but Rambler Digital Solutions has improved and simplified it even more. Let’s take a look at the main changes:
- Assembly functions which Wireframe was responsible for are moved to Configurator now. So, after this modification, Wireframe is in charge of Routing and nothing else.
- The router has become global
- All data transfer is executed according to ModuleInput and ModuleOutput protocols
- View is not only ViewController now; it acts as a presentation layer containing several parts (View, DataSource’s. Delegate’s, etc.)
- A new service layer has been added to a module with dependency injection into interactor. Thanks to this improvement a module interacts just with required services (they make networking, storing, collecting, etc.).
Thus, the VIPER is quite a useful architectural system, allowing to distinguish all of the logic, so that every class performs only actions it is responsible for.
This is the first part of our article about using and adapting the Viper architecture. In the second part, we're going to discuss structuring and creating project skeleton for VIPER.
Follow our blog to learn more about the architecture of VIPER!