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’.
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.
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!