A few years ago, we had a lead engineer who introduced us to VIPER, an architecture for iOS. Willing to give it a try, we ended up using it for 10 of our most active features. This is a story of how VIPER came into our codebase, and how it eventually morphed, and left. Hopefully, by sharing our journey others can learn from it. VIPER might be right for you, but it wasn’t right for us in the end.
VIPER is a backronym. Something that has to be explained every time you mention it probably is not a good idea. Roughly it translates to: View, interactor, Protocol, Entity and Router.
The ways VIPER was implemented in our system was:
- Interactors – almost all business logic was encapsulated in this- how components interact within the architecture
- Router – includes methods for all exits, modal launches, or other navigational changes
- Data source – or, API handler, gets data and reloads data, includes all methods for data management including loading. This roughly maps to the E in Entities.
- Assembler – a class to put the entire thing together. Also named “wireframe” at times or “factory”
On the plus side…
One of the key benefits of VIPER– binding views with data elements — was not available in our implementation because we weren’t using Swift and Realm at the time. For complicated forms, this does make the code more sensible, as those tend to go with longer controller classes, with more validations and business logic.
VIPER is very testable, and that was one of the reasons it lasted so long in our codebase. You can easily mock up the data class, ignore the view class, and test the business logic in the interactor, router, and view model. For complicated forms, this was really smart.
On the negative side…
One of our first misgivings was: why so many files? For a given feature, 6 files were a minimum. If there were more view models, screens, or modal screens, this would quickly increase. VIPER was born from the “fat controller” problem. So it does limit the number of code lines in a single class. Perhaps in Swift, this would be easier, but for Objective-C, since you need a header and implementation file, this makes it a problem of files X 12.
Since you’re out of the normal MVC that Xcode recommends, accessing a view controller from inside a separate class to do router-like behaviors like ”launch a pop-up” become very complex. Also, get very familiar with delegates and protocols, and objects that are essentially just protocol- something we are finding very difficult to move to Swift.
- Never use VIPER in Objective-C, very wordy and overly complex
- Highly recommend for isolating complex business logic for testing- if MVVM isn’t enough
- Do not recommend for nested architectures, too complex.
One of the major risks to look out for: nested vipers. When one architecture “nests” another model inside of it. This can cause a lot of issues and complexity around referencing a new model, instantiating it, then routing to the specific screen or navigation you need. When you’re doing this two or three deep, the number of files and recursions get complex and buggy. One of our first initiatives when we decided to leave VIPER was to collapse or flatten the nested architectures.
We’ve moved our codebase now to Swift and MVVM, testing view models and models, and limited UI tests. So far, this has helped with training new engineers, and the testing coverage is maintained. We still have a few single-generation VIPER architectures lingering around, either to switch to Swift and keep, or move to MVVM.