comments 26

#8 VIPER to be or not to be?

This time we would like to tell our story and share the experience about VIPER. Especially about good practices, how we deal with some specific cases and about our recommendations. We count on the comments with your experience!:)

In this article our goal is not to talk about VIPER’s rules and explain every component of VIPER from scratch. Many sources with very good explanations about these things are already available:

We would like to show what we have learnt, what have been the biggest challenges during working with this architecture. At the beginning we had many questions to answer. Last year we started a new big project: a conference application (consisting of an agenda, a participants’ list, a speakers’ list, a news’ list, etc.) and then the story began…

Ready for a fast ride with VIPER?

Viper car
Viper car

Why did we choose VIPER ?

Note: If you have doubts about which architecture to choose for your new project, maybe this site will be helpful for you. Our reasons for using VIPER:

  • VIPER architecture should be used for applications whose requirements are very well defined at the beginning. Fortunately, in our case they were. If you know that your screens and business logic can be strongly affected by a product owner, VIPER may be not a perfect solution for you. Even a small change can impact your all module files (a view, a presenter, an interactor…). Redesigning can be a very time-consuming task. A better solution would be just creating a new VIPER module.

  • Our project was quite big. By setting one module you have to generate and write a lot of (it seems sometimes) redundant code. Very often you have to just pass data between all VIPER components from a view to API manager, and then come back with some returned data to view. That’s why there is no point in using this architecture for very small projects.

  • VIPER (without a few exceptions:) ) has very clear politics about responsibility for each component. It helps with reducing amount of code in files and putting into the right place according to a single responsibility principle. Additionally a project is very well structured what enforced on every developer in our project using the same practices. If a new developer joins the team, he should really fast adapt to VIPER and from the other hand it is less possible that he destroys something in this kind of architecture:)

  • If you work in a team of for example 3 developers, then everyone can work on one module. In this way you can easily divide your work.

  • In our case we also had very good defined features and screens from the very beginning so it wasn’t a problem to translate it into VIPER modules.

  • On the basis of VIPER principles, everything in one module is very well separated, so it creates good environment for unit testing. Look at this article regarding more info about TDD in VIPER.

  • Finally, we wanted to check and try out a new architecture!!! Hoping to avoid a bad decision:

Massive
Massive

which starts from MVC and ends up with MassiveVC.

Project structure, folders and VIPER modules

Do you remember all VIPER module components? Our proposal is based on this website, where used components are called "Services". You can find more about services in a section Services.

VIPERDiagram
VIPERDiagram

How to translate this into project files? Every component should be translated into a separate folder and class:

FolderStructure
FolderStructure

Next question is: what is the best candidate for VIPER module? Basically the easiest approach is to translate one screen (or big feature) into VIPER module. For example:

  • Login screen -> Login Module.
  • Participant List -> Participant List Module

Do you imagine creating whole stack by hand every time? Fortunately, there are generators:

VIPER modules Generators

If you really want to make your application based on VIPER architecture, do not even think to make it all manually. It will be a disaster! You need an automated process to create a new module (Trust me:)) . First of all, you can download one of available VIPER generators here:

In our project we took the first one and customized it further for our purposes. For example, we have added test files for Interactor and Presenter. Our custom vipergen tool is available here:

That customization required a little bit of modifications in Ruby. Note, that it was only because we wanted to have an auto-inserted swift module name in all unit test files.

You will probably just need your custom templates. They can be easily created by copying and editing one of existing folders with templates. Just follow this instruction to learn how to add a new template. You have two options, send a PR to an existing repo or create your own fork and gem for more control. Git repositories of vipergen provide more useful information.

We did not use other solutions much, but Generamba seems like a well supported tool and provides nice setup steps from CLI. Make sure that you have checked out all available solutions to find one which one suits you best.

Sending information between VIPER modules

From the very beginning we thought how to deal with passing data between VIPER modules, there was no clear answer. It was very helpful to start reading below topics:

Finally we decided to send information from one module to Presenter in the second module (it seemed to be the best natural way without breaking VIPERs principles):

PassingData
PassingData

What does it mean in context of code?

SpeakerDetails module is initialised based on a class method invoked in SessionList wireframe, then in above method a presenter of SpeakerDetails has knowledge about what session user has selected.

VIPER Entities and Core Data

First of all, we decided to create our own core data stack. Why not to use external libraries to make this quicker? Because we wanted to have full control of our persistence store.

Our Core Data stack have two managed object contexts: one on the main thread and one on the background thread. Both connected to the same persistent store coordinator. Each context operates independently of the other. Changes are exchanged by merging the did-save notification:

CoreDataSchema
CoreDataSchema

Note: This idea was taken from a great book: Advanced Core Data, where you can find much information about how to setup your own CoreData stack with many alternatives. We strongly recommend this book:)

But what about entities?

Entities are sent between VIPER components but not as CoreData’s NSManagedObject instances. Managed objects are accessible only in local managers, where they are converted to entity ( in convertToEntity() function) and passed to an interactor:

CoreDataConvertToEntity
CoreDataConvertToEntity

Why not to send NSManagedObjects? Because in this way our application is separated from data model and data layers. In this way we keep Core Data where it should be: at the data store layer.

What data type represents entity? In our case every time it is just a struct:

which is small, immutable and thread-safe, just perfect:)

Dependency Injection

Use of VIPER architecture gives great possibility to apply dependency injection. For example, let’s consider an example of a local manager:

Injection in the constructorn in this class gave us two advantages:

  • We have a better sense what’s going on in this code. We see immediately what dependencies our class has
  • On the other hand, our class is prepared for unit testing

Another example for a presenter:

When using VIPER architecture a good practice is to use DI in every component. We will show in Unit Test section a few examples how this approach can really help us during testing.

What is really Wireframe?

In our case wireframe has two functions:

  • It initialises instances of each VIPER’s component and wires them up:

  • The second responsibility is to navigate and present other modules:

Nothing more:)

Unit testing

We have to admit that we didn’t have a big experience with unit testing before. To make first steps in this field: we started from testing interactor and presenter, because interactor contains main business logic and presenter contains logic responsible for preparing data before displaying. These components seemed to us more critical than others, but it was just our subjective opinion.

Libraries used by us for unit tests:

In VIPER every component of a module is strictly separated what creates a very friendly scenario for adopting unit tests in terms of single responsibility principle:

UnitTestMock
UnitTestMock

As we can see, by separating components in our test we can focus only on testing responsibility of interactor. The others components which talk with interactor are just mocked.

How does it look like in perspective of code?

Services

What are services? They are independent components, separated from strict VIPER components. One Service can be used in multiple modules. As it is mentioned here, services are not necessary but can be really helpful, especially in keeping high cohesion. Their responsibilities are for example:

  • dealing with Calendar (CalendarService)
  • dealing with Adress Book (AdressBookService)
  • managing Keychain (KeychainService)
  • Person Service (PersonService) – manages downloading user data like a photo etc. In this case a service has also an instance of apiClient needed for network requests.

Service in action:
Service

How to deal with listening changes from backend?

In our app we have a couple of lists to display that should be auto-refreshed. Our SynchronizerService does the heavy work of downloading and putting JSON responses into CoreData in background. It happens every 1 minute.

CoreData Update
CoreData Update

CoreDataService is the first of important components that initiates the whole process of updating content.
It has a save method that accepts an optional parameter to send notification:

CoreDataService also listens to NSManagedObjectContextObjectsDidChangeNotification. When this context notification is triggered and save function was called with notifiy flag set to true, a custom NSNotificationCoreDataStackDataUpdatedNotification is fired. That is all regarding the participation of CoreDataService in the process. Clearly you may want to have a more precise notification mechanism. For example, each CoreData entity would have own notification. So far a simplified approach works for us.

The next participant in the update flow is an interactor. We had a very long discussion who should listen to notifications sent by CoreDataService and act on it. Interactor as a main logic engine is our arbitrary choice. Interactors in modules that require data refresh inherit from DataUpdateListener class:

The dataUpdated(notification:NSNotification) is overridden in interactors. There, as you may expect, standard VIPER flow kicks in. Interactor after receiving notification asks its local data manager for data. This fresh data is then passed to presenter to preprocess. Then presenter passess it to view to display. Voilá!

APIs between VIPER module components are defined in a separate protocol file so that bidirectional communication is always possible (if we define it like that). Using a closure for completion would block us from pushing data from interactor to presenter:

A good old delegate pattern works better in our update mechanism. Not only presenter can ask interactor to get data for it, but it can also initiate the whole update process:

Looks clean and solid, right?

So when to use VIPER, and when not?

The answer is always the same: it depends.

We hope that below block diagram will help you in answering this crucial question (but of course as always, this is a very individual issue):

ViperOrNot
ViperOrNot

Summarizing

Starting to use VIPER can be a big challenge, especially when it is your first project in this architecture. We hope that after reading this article most of your doubts and questions will disappear.

In our case we have practiced git flow with pull requests. It was very helpful, especially at the beginning, when every developer in project could observe carefully what colleagues were pushing to the repository. It would be a disaster, if everyone would make his own version of VIPER without consulting and watching what the others were doing. When we have inconsistency in some rule, then we immediately call the meeting to brainstorm and to choose solution together that everyone agrees to use.

VIPER determines very generally how to build an application. We encourage you to stay open minded, never stop to customize and optimize each component. Discuss each doubt with your colleges.

VIPER requires continuous improvements, we hope that in our new projects, we will do better having experience from our first project.

On every step of your development be very careful not to turn architecture into a nightmare, especially at the beginning, when you have to set up the whole project’s structure. One mistake in an architecture can turn into a chain reaction in multiplying further mistakes. That can cause a lot of tedious work.

Finally, VIPER gave us an opportunity to implement unit tests very easily, without big experience in this area:) Thanks VIPER!

What is your opinion about VIPER? Share your comments! 🙂

Interesting resources

26 Comments

  1. srinadh

    Thanks team, i have been looking like these details explanation on VIPER. i will read and will give my feedback. but where to we download sample project. so we can understand some more deep .

    Thanks for your effect on VIPER.

    • Michał Wojtysiak

      Hi Pawel. Good question.
      This is a perfect case to show in sample VIPER app that we plan to release.
      To explain it shortly. When there is a need to present a new ViewController we do the following:
      1. Wireframe function of presenting module has a parameter ‘fromView’ where we pass current ViewController.
      2. In this function we instantiate presented module with method that returns fresh ViewController to present.
      3. At this point we have both presented and presenting ViewControllers available in the same function body.
      That’s enough to perform various kinds of navigation.

      Best regards,
      swifting.io

    • Michał Wojtysiak

      Hi Miguel,

      We have read about other clean code architectures (uncle Bob’s comes to my mind) and appreciate their higher flexibility that VIPER provides. However we do not have practical experience with them yet. Thanks for the link to clean swift. It will be of great use when starting new project.

      Best regards,
      swifting.io

  2. Ruslan Remenkov

    One of the best articles about VIPER!
    Thank you so much! 😀

    …but I still don’t see the whole picture. Can you share simple Swift VIPER project with 2 modules: 1 will be simple, and 2nd will contains a couple of submodules with data transferring between each other?

    PLEASE! 😀

  3. Hi, Thanks for great post. You wrote that “Basically the easiest approach is to translate one screen (or big feature) into VIPER module.”. According to your experience, Do you prefer using Viper architecture on screen basis? or feature basis? I mean, instead of Login module, we can make Auth module consisting Login, Registration, Forgot Password and etc. So Auth presenter will manage 3 views (LoginView, RegistrationView and ForgotPasswordView), AuthInteractor will manage again 3 types of interaction instead of 1. Why I am asking for is that, we are about to start a project (about 50 screens ), and 50 Viper modules seem us hard to maintain. What’s your opinion according to your experience?

  4. Bogusław Parol

    Hi,

    first of all thanks for so great article! We are wondering if we should implement our new product using this pattern. Some conditions are met: requirements are well defined and it’s a big project which will be maintained in the future. The target platform is iPad and we are going to implement rich screens that can be easily divided into components. There will be couple of such components visible on the screen. The problem is that we need exchange data between these components. Each component need to get some initial data, then it will load more data from the network or local db and present it to the user. Moreover, each component should have possibility to initiate actions, which will affect entire screen. How would you organise architecture in such case? Thanks!

  5. Pingback: Modern iOS app architectures | Ackee blog

  6. Lukas

    Great post. One issue that I see is the usage of anti-pattern Bastard injection. Properly you should use composition root, which provides sometimes a lot of challenges but forces to use correct DI.

  7. XuYanci

    In u example , when u present or push viewcontroller , u need to make an wireframe instance ,

    just like : [in an viewcontroller]
    func gotoLogin() {
    var loginWIreFrame:WireFrame = loginWIreFrame.init()
    loginWIreFrame.presentViewControllerFromVC(self)
    }
    -> U must keep loginWireFrame alive … . if not it will be release later .

    i resolve this by just presentviewcontroller in the wireframe . (use class method ) .. just keep the controller alive

  8. llodi

    Thanx for articles! Is it good for cleaning principles to separate behaviours like viewIsLoad in general protocol? And if you need additional methods for your module you can create own protocols and add this to it?

  9. Pingback: Free Piano

  10. Pingback: Free Piano

Leave a Reply


*