What I've learned switching from MVC to VIPER

As an iOS developer, you certainly know that the basic programming architecture for Xcode is MVC - Model-View-Controller. But is MVC really the best way to make mobile apps? I checked out by giving the VIPER design pattern a try. Look what I found out.

MVC: Model View Controller gone Massive

After a few Swift projects, every developer will find the limitations of MVC. There are even jokes about the abbreviation itself. You must have heard about the Massive View Controller. This is because View Controllers in MVC are heavily occupied. They take on the roles of Views, Controllers and even business logic.

So, the first problem with MVC are overgrown View Controllers. For a developer who wants to refactor code, add something or just see how an app works, it often takes lot of time to just find what they are looking for.

Even in small apps, View Controllers are usually too big. 200 lines of code is considered a moderate, but, sadly, a quite frequent number. Needless to say, the bigger View Controller = the more uncomfortable one and likely to cause mistakes. Which leads us to yet another disadvantage.

The second big problem is the natural tendency of MVC to generate bugs & problems. This stems from the classic architecture of creating and managing View Controllers. Why? Because, again, they take too much duties.

Suppose you have code that is responsible for showing Views, responding to users’ interactions, business logic & all the stuff-in-between in the same place… Well, that won’t end with ‘happily ever after’.

View Controllers usually take too much tasks, which is why they are called Massive View Controllers

This is why many developers move away from MVC in favor of the MVVM, MVPM, and - last but not least - VIPER.

What made me try VIPER

The turning point for me were 3 similar apps that use the same business logic:

  • Crossfit Copenhagen,
  • jo:ga,
  • and the most recent one Crossfit Copenhagen Booking (for the German subsidiary of our client).

These apps vary mainly in look, but connect with the same API. Also, they generally have similar layouts with minor differences.

jo:ga and Crossfit Copehagen logotypes

The question I kept asking myself was:

Should I create 3 apps & copy business logic for them?

That was nonsense. I was considering to create an MVC app with targets - every target is different app. But having experience with MVC I didn’t want to duplicate problems with overloaded View Controllers.

So I tried VIPER.

My ride with VIPER. A case study

I don’t want to discuss the pros & cons of VIPER here - you can easily find them on the Internet.

If you still need a proper introduction to VIPER, read this article.

I wanted a clean and effective architecture that would create code that could have been used for different apps.

Split ViewControllers for the same business logic

VIPER looked great because of the ability to split View Controllers for Interactor, Presenter & View. This was very important, because my apps use the same logic, but have different layouts.

The choice was perfect. VIPER allows to create different Modules with View, Presenter, Interactor & Router. So for my apps all I needed to do was to create the same logic code & add Views as targets.

Splitting ViewControllers in practice

How does it look? In each Module I created targeted Storyboards - one per app. There is also one View Controller but with targeted extensions. So if a particular Storyboard needs specific code (e.g. for defined views), it can be found in extensions. Business logic is managed by Presenter.

Let’s check an example. The main view for all 3 apps is for managing & checking classes booked by a user. In CFC & CCB you can access class details by tapping cell in TableView, but in jo:ga you need to pass Alert View Controller first with a few options (see details, add class to Calendar, cancel class etc.).

So after tapping a cell in View Controller, information is sent to Presenter. Depending on the target, Presenter launches:

1) router segue to Details View Controller 2) Alert View Controller with more options. But the basic View Controller just sends a message that the cell (at IndexPath) was tapped. All further management is done by Presenter that calls specific functions in Interactor.

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    output.didSelectEvent(atIndex: indexPath.row)
}

I have many more examples of VIPER making managing View Controllers more simple.

One of them is that in Crossfit Copenhagen & Crossfit Copenhagen Booking, the app uses specific alert views, whereas in jo:ga the users' actions are performed immediately. If I were using MVC, I would need to implement different cases in each View Controller. Thanks to VIPER I just send information from View Controller to Presenter and then decide what to do. It’s not only a much better option but also cleaner.

What I didn’t like in VIPER

Not everything is shiny, though.

VIPER is great for larger projects, but it also has its dark side. For me, it’s segue stuff.

In classic MVC and Apple way - it’s very easy to use Storyboard segues. You don’t need to think which Controller leads to another. This is managed by Xcode. Also, even when you use segues programmatically, it’s very easy because they’re nested in View Controllers. But in VIPER much more is different.

First, you don’t use Storyboard segues. Second, you don’t use segues from View Controllers. You use a special class - Router.

For each Module, there is a different Router. So when you want to pass data between Views or simply let user navigate between Views - there is much more work to do. You can’t just use functions like present(_ viewControllerToPresent), because Router doesn’t know from where it should start.

This is why in Router functions you need to claim: current View Controller, current Navigation View Controller or Tabbar View Controller (if it exists), then claim destination View Controller and then - if it’s needed - pass the data. For basic stuff like View Controller -> View Controller, using Router is a piece of cake.

But when you, for example, use Navigation View Controller nested in Tabbar View Controller, transitions can be more complicated. And you must always remember that in unwind case Router also doesn’t know from where and where it presents Views.

On the other hand, using Router + targets & extensions was very helpful in my case. From View Controller I just call Presenter and then decide what to do with segue. In the example mentioned above, Presenter is recognizing, if there is a need to call Router or to show alert before that.

I must also say that using VIPER is a bit confusing for a beginner. In the classic MVC you have almost everything in one View Controller. You only call Model stuff etc., but the basic logic is usually mixed with View code in one file. When I started using VIPER, it took me a while to get accustomed that a lot of stuff was split between Presenter, Interactor, Router & Entities.

My colleague claimed that you don’t even need to use View Controller and leave all the design stuff for the end phases of making an app. That’s true. Other than in MVC, view stuff is minor thing. You can build almost an entire app (managing func, communicate with API, wrappers etc.) without touching View Controller. When I got used to split files and different navigation, I found out that VIPER is much more clean and structured than the classic Model-View-Controller.

Wrap up

In case of CFC, CCB & jo:ga, VIPER turned out a great solution. Instead of 3 projects, I have one with lots of shared code right now. Also, the code is cleaner, properly ordered and, I think, easier to view. In case I need to expand the app in the future, I can just add new Modules without changing an existing project. Thanks to wrappers (mostly located in Entities), I can easily change the used tools (API connectors, cache management etc.) without interfering with the rest of the code.

In my opinion, VIPER can be great not only for big projects, but also for the small ones or the ones that you know will develop.

Currently I finished refactoring my Ector app for VIPER and I found out that now the app is more stable.

I don’t think using VIPER is necessary for small projects, but you never know when a small project will grow into something much bigger.

Also, I didn’t mention about tests - which is one of most important parts of VIPER. I’ll do this in the next blog post.

Have an idea for a mobile app and wonder about giving VIPER a try? Drop us a line, we will gladly learn read about it and help you make the right decision!

Recommended reads:

Contact us

* Required fields