Background

Consistent design patterns help Etsy move fast and deliver better features to our sellers and buyers. Typically we built features in a variety of ways, but found great benefits from the consistency VIPER brings.

Inconsistent development practices can make jumping between projects and features fairly difficult. When starting a new project, developers would have to understand the way a feature was implemented with very little shared base knowledge.

Just before we began working on the new Shop Settings in the Sell on Etsy app, VIPER was gaining steam amongst the iOS community and given the complexity of Shop Settings we decided to give this new pattern a try.

While the learning curve was steep and the overhead was significant (more on that later) we appreciated the flexibility it had over other architectures allowing it to be more consistently used throughout our codebase. We decided to to apply the pattern elsewhere in our apps while investigating solutions to some of VIPER’s drawbacks.

What is VIPER?

VIPER is a Clean Architecture that aims to separate out the concerns of a complicated view controller into a set of objects with distinct responsibilities.

VIPER stands for View, Interactor, Presenter, Entity, and Router, each with its own responsibility:

View: What is displayed

Interactor: Handles the interactions for each use case.

Presenter: Sets up the logic for what is displayed in the view

Entity: Model objects that are consumed by the Interactor

Router: Handles the navigation/data passing from one screen to the next

Example Implementation

As a contrived example let’s say we want a view that displays a list of people. Our VIPER implementation could be setup as follows

View: A simple UITableView set up to display UITableViewCells

Interactor: The logic for fetching Person objects

Presenter: Takes a list of Person objects and returns a list of only the names for consumption by the view

Entity: Person objects

Router: Presents a detail screen about a given Person

The interaction would look something like this:

  1. The main view controller would tell the Interactor to fetch the Person entities
  2. The Interactor would fetch the Person entities and tell the Presenter to present these entities
  3. The Presenter would consume these entities and generate a list of strings
  4. The Presenter would then tell the UITableView to reload with the given set of strings

Additionally if the user were to tap on a given Person object the Interactor would call to the Router to present the detail screen.

Overhead & the issues with VIPER

VIPER is not without issues. As we began to develop new features we ran into a few stumbling blocks.

The boilerplate for setting up the interactions between all the classes can be quite tedious:


// The Router needs a reference to the interactor for 
// any possible delegation that needs to be set up.
// A good example would be after a save on an edit 
// screen the interactor should be told to reload the data.
router.interactor = interactor // This is needed for the presentation logic
router.controller = navigationController // If the view has any UI interaction the presenter 
// should be responsible for passing this to the interactor
presenter.interactor = interactor // Some actions that the Interactor may do may require 
// presentation/navigation logic
interactor.router = router // The interactor will need to update the presenter when 
// a UI update is needed
interactor.presenter = presenter

As a result of all the classes involved and the interaction between them developing a mental model of how VIPER works is difficult. Also, the numerous references required between classes can easily lead to retain cycles if weak references are not used in the appropriate places.

VIPERBuilder and beyond

In an attempt to reduce the amount of boilerplate and lower the barrier for using VIPER we developed VIPERBuilder.

VIPERBuilder consists of a simple set of base classes for the Interactor, Presenter and Router (IPR) as well as a Builder class.

By creating an instance of the VIPERBuilder object with your IPR subclasses specified via Swift generics (as needed) the necessary connections are made to allow for consistent VIPER implementation.

Example within a UIViewController:


lazy var viperBuilder: VIPERBuilder<NewInteractor, NewPresenter, NewRouter> = {
 return VIPERBuilder(controller: self)
}()

The VIPERBuilder classes intended to be subclassed are written in Objective C to allow subclassing in both Swift & Objective C. There is also an alternative builder object (VIPERBuilderObjC) that uses Objective C generics.

Since the boilerplate is fairly limited the only thing left to do is write your functionality and view code. The part of VIPERBuilder that we chose not to architect around is what code should actually go in the IPR subclasses. Instead we broke down our ideal use-cases for IPR subclasses into the Readme.

Additionally, we have an example project in the repo to give a better idea of how the separation of concerns work with VIPERBuilder.

The iOS team at Etsy has built many features with VIPER & VIPERBuilder over the past year: shop stats, multi-shop checkout, search and the new seller dashboard just to name a few. Following these guidelines in our codebase has lessened the impact of context switching between features implemented in VIPER. We are super excited to see how the community uses VIPERBuilder and how it changes over time.

Etsy