Architecting with VIPER — The paper of the View in a VIPER scene
I started to play with VIPER around 2 years ago. At first it was a little confusing what each element of that architecture was responsible for. And in fact distribute responsibilities is one of the main keys of VIPER. This post brings a little of my thoughts around the responsibilities of the View. Also, I decided to expose some practical questions, which means applying VIPER in real world situations. I find this type of approach more helpful than just abstract discussions.
According to a post co-written by Jeff Gilbert, the creator of VIPER, the View (1) is the component that display what is told by the Presenter and (2) relays user input back to the Presenter. This gives us a base to start working on what is the actual View’s job. Let’s start with the user inputs.
The view is the one responsible to deal with the user inputs. The simplest example that can be imagined is a tap in a button. Whenever that button is pressed, is the view that will be first notified. And then the view will inform the presenter about that event, without any filter, without any condition. The presenter is the one that will decide what will happen after.
Table view delegate
The view paper related to the user input sounds really simple, and for many different examples it really is. Things start getting blurred when more complex examples arrives. I've seen some people setting the presenter as the delegate of a UITableView. I have done it before myself. They do it in order to make the presenter to responds to actions such as didSelectRowAtIndexPath. I mean, at some point yes, the presenter is supposed to receive that action. But not now, after all, the view has to receive first all user inputs. So I guess the lesson is: let's try to make the view to receive all user events, and then let's propagate these event to the presenter. Even if there's a shorter way.
Long presses in cells
I saw other examples of misunderstanding of delegations before. I remember this one time that a project had a requirement of to intersect long presses on UICollectionView's cells. And there was a whole mess around that. Cells intersecting events and then reporting it to the presenter. Doesn't it sounds weird? Isn't the view that will have to be notified about this event first? There's a clear break in the VIPER attributions here.
A possible solution for long presses
I can imagine one possible solution to implement this requirement and still have the view being notified first about the user inputs. We could configure, on the view side, a long press for that UICollectionView. Whenever the long press is dispatched, we find the cell that was pressed according to the position of the event, and then we propagate this information to the presenter. We could create, let's say, a function called
didLongPressCellAtIndexPath(_ indexPath:IndexPath) on the presenter side. This way the view is the one receiving the event first, and informing the presenter which cell was long pressed. Then the presenter do whatever it wants to do.
Display what is told by the Presenter
Now we're going to discuss the other attribution of the view. I think the best way to introduce you to the discussion is to base it on an example.
The weather app case
I may have a blurred case to introduce. Let’s say you have a UITableViewController for a weather app. The cell at section 0 and row 0 will change depending on the weather that day. If it's sunny it be a big cell with a sun image called SunnyCell. If it's raining now the cell presented will be the RainyCell. I have a couple questions to lead us to find the right delegation for each of our VIPER members, applied to this case.
- Who will define which cells are going to be displayed when the view appears?
- Who’s going to instantiate/dequeue those cells?
- Who’s going to fill those cells’ content?
The view is passive. It shouldn't ask anything to the presenter. How it will work then? Most of us would have the idea of:
- Ask the presenter which type of cell to display for a specific row, and then instantiate/dequeue it.
- After having a reference for a cell, send that cell to the presenter to fill it's content (labels, images, etc).
There's a mistake with this approach. We can't be asking things to the presenter. By definition the view (1)display stuff and (2)propagate events.
To avoid having the view asking the presenter for information, we could pass a reference of the UITableView to the presenter? This way the presenter takes care of the rest! But still sounds weird that the presenter will instantiate/dequeue the cells. The presenter should have view logic to prepare content to display. It doesn't say anything about instantiating view elements. This sounds mostly like a duty of the View.
Let's answer the question we made before, maybe they'll lead us on solving these problems.
Who will define which cells are going to be displayed when the view appears?
The weather that day is what will say which cell will be displayed at the top of our table view. The interactor is the one that will have a direct communication with the services offering this information. Let's imagine that this communication will be synchronous, just to make our problem less difficult. Interactors shouldn't contain any view logic. The presenter may have a function that will converter the weather that day in a cell type. Not the cell itself, just the type. The answer for the question would be: the presenter, with some help of the interactor, will define the cell type displayed on the top of the table view.
Who’s going to instantiate those cells?
We discussed before that this isn't a presenter task. Also the interactor doesn't know anything about UIKit. The wireframe is mostly related to navigation. The View will take care of instantiate/dequeue the cells of the table view. We are only facing a problem where the view is asking information to the presenter. It shouldn't. We'll have to work on that.
Who’s going to fill those cells content?
The presenter is the link between the information and the empty views. It will fill these cells whenever it has access to them.
We have fair answers to our questions, and we can now think about our architecture!
The final architecture of the weather app
Every situation may require a different approach to solve problems. When dealing with VIPER and this weather app problem, there may appear dozen of different approaches in order to define the architecture of this "app". I will introduce you guys to one that I think will respect all VIPER definitions, but in real world maybe is not the perfect approach.
Explaining a little what is happening in the schema above:
- UITableViewController's viewDidLoad is called. The view notifies the presenter that this step of the life-cycle was reached.
- Knowing that the view is ready, the presenter tells the interactor to start loading it's content, in our case, the weather information.
- After the content is loaded, the interactor notifies the presenter that the content was updated.
- The presenter gets the updated data and map it into cell types — that could be, for example, a list of enum cases.
- The presenter calls the view to update it's data, passing to it a list of cell types to be displayed.
- The view will receive the call to update the content. It will store the array of types somewhere and call the UITableView's reloadData. Now, whenever the cellForRow is called, the view knows exactly which type of cell is supposed to be displayed at a specific index path. And most important: The presenter is the one that told the view this information, with no need of the view to ask it.
A real world approach
Even though the example that I gave sounds a fair way to solve the problem we have been discussing, in real world it doesn't happen often. One thing I would possibly change here is the fact that the view keeps a list of cell types. This list could become big depending of the type of content displayed. Imagine for example the Facebook or Twitter feed. The list of cell types will get big soon.
I would replace this approach on forcing the view to ask the presenter the type of cell to be displayed in a specific index path. It will help on this problem, and that's no big deal on the view be asking the presenter for this information, after all. It will break the VIPER definitions, but we'll have a still clean architecture and a memory respectful code.
Thanks for reading! I hope this article has helped you to clarify some ideas. And since architecting is not an exact science, I would love to receive some feedbacks on the approaches we took over the text.
Make sure you're following me here, so then you can receive notifications for the next articles. I guess the next one would be to discuss the wireframe.