iOS VIPER: Presenter x Interactor responsabilities in different situations
I think one of the most relevant questions regarding VIPER is about the separation of tasks between its layers. Its's clear enough, according to our previous article that View is only responsible of displaying our UI with proper content and build hierarchy; Router takes care of all context changes(popups, modals and change of screen); Entity is a data model describing some entity from our business(usually a struct or enum).
However, a lot of people come to me asking things like:
- Field validation should be located at Presenter or Interactor?
- Should Interactor be only responsible of handling data from external sources like data persistence and API's?
- When do we need to pass through the Interactor?
- Data inputs from other scenes should be injected into Presenter or Interactor?
For short, several developers don't know a 100% how to separate Presenter and Interactor and what exactly are use cases. That's the purpose of this article, to answer these questions and provide a more accurate definition for each of these layers. I will provide some examples. But keep in mind that I am not the source of truth and different people may disagree with me by having a different point of View(if you think different, please, paste your point of view as a comment). What I am about to show you is just the way which I would structure my own project. Hope you like it!
What are use cases?
For many people this shall be an obvious question, use cases are like a sequence of interactions in the application that receive an input and this sequence also produces an output to be consumed by the next scenario. What folks usually get lost in is about if it's related to UI or not.
Let's imagine you have a sign up screen where some personal information is requested: you need to provide your name, your email, a password and a password confirmation. Simple as that. As normal, this scene has some validation rules before submitting the inputs to back-end:
- The submit button should be enabled only when all the four fields are valid.
- Name is valid if user types first and last name(Two words separated by a black space).
- E-mail should have an e-mail format.
- Password should have at least one uppercase, one lowercase, at least a number and a special character.
- The confirmation should match the password.
These validation rules should be handled by Presenter or Interactor? That's the big question. Let me answer that in my way:
There are three questions you should ask yourself about this scenario:
- Do those operations manage a data model from our business core(data manipulation, don't confuse with presentation logic)
- Do I need to fetch data from the web or manage data persistence or any kind of external source or API?
- Do these operations request some kind of data logic which is not related to UI(no formatting nor anything related to how user shall see the interface)?
If you answered YES to any of these questions an Interactor call is certainly needed.
A use case is much more than presentation, it's business logic. I have a more precise way of defining that:
A use case is any interaction with the system that cause changes to our Entity-Relation diagram, being it related to the whole ecosystem or just a scene scope.
For this scenario where we are only validating when when a button should appear, there is no business core being changed, just presentation, so everything can be executed inside
Feature flags and project configuration
If you still don't understand the purpose of feature flags, I strongly recommend you to read this article. Feature flags dictate a behavior in your application that may be controlled by an outsider or by its own developer when testing some specific scenario in an environment. People(me included) discuss a lot about if that belongs to our presentation logic or if fetching a configuration is part of some use case. The answer is: it's just presentation logic.
There was a time in a project that I worked years ago when I faced a problem that I needed to fetch an environment variable that was different depending on the target I was working. This project had three white labels(three apps, three design systems, three configurations, same project) and each one had a different value for this variable. This variable was just a string describing the section order for a help center screen. A configuration piece of data is related to the environment you are working, that's how that application works and there is nothing that can change it(at least in this scenario). So I fetched that from our
FeatureFlagManager class from Presenter itself and then proceeded with the logic. The code was something like:
People may think it should belong to Interactor and I respect them, but regarding my concept and answering those three questions:
- No interaction with a data model around global application or help center scene.
- Ok, some people may think of that as an external source, but it's different, it's predefined configuration that shall be the same for this white label or environment all the time. So no, we are not relying on external sources.
- We are not applying any logic(there may be logic inside or
FeatureFlagManagerbut restricted to there, not our scene), so no.
We answered NO to all three questions, so it should be applied to
Presenter , not
Parsing environment data
In this same example I described above, I needed to take this
sectionOrder string and parse that in order to translate to a more readable array of section enum cases with associated values to fill our screen. Each section corresponded to a
UITableViewCell and their content was an associated value to be filled. These were the sections:
- Social Networks(different networks for each app with different URL's)
- e-mail(varied for each app)
- Customer portal
My enums were something like:
There was a logic restricted to this screen regarding how that string from the configuration file would be parsed into a list of section enum cases. So, discussing with the iOS team, we decided to place that method within the
Interactor and fetch that from the
Presenter . The enum case array would dictate what are the cells our Presenter would created according to each case and value:
So our Presenter is responsible for fetching the given string for sections order, then it's parsed by the
Interactor to describe better what are the sections. When the output is received,
Presenter creates cells according to that and passes to our
Sometimes our screen doesn't even have any logic nor interaction with the user. Screens like error, success, coachmarks, tutorials and warnings where the only option for the user is confirming or maybe closing.
These cases rely on static texts or numbers to fill our UI. For these cases I wouldn't even recommend an
Interactor layer since we are not triggering any use case at all. However, some literatures recommend creating an empty
Interactor class without any method. In my personal opinion, keeping an empty Interactor would only increase unnecessary complexity and can be discarded since it's optional.
For that, you may keep all the static texts in the Presenter:
Since in this case there is no interaction that could change our UI, a
ScenePresenterOutput protocol is not required.
All the mobile and web applications retain analytics events being triggered according to user interactions. If you don't know what are analytics, it's basically a way to log each user interaction event or variable output from an external source, like a presented screen, a succeeded or failed API call, a clicked button or a scrollable content that achieved the end of screen, but it's a topic for another article. What we are discussing here is about where analytics events should be triggered: Presenter or Interactor? The answer is: Presenter.
Not every time we shall have an
Interactor as discussed before and not always a user interaction will trigger an use case. With that in mind, all the UI events shall be logged in the Presenter itself. The only exception for that is when we are logging an API result, that may be logged by the Interactor since there is the place where we directly receive results.
Where should I first inject data from previous scene?
You may be thinking about where should you save the input data. This data may be used to directly present some content, maybe for fetching remote data from an API or it might be used to imply in an use case, user interaction output. The answer is: Both layers work.
For example, if you are just injecting some name that may be displayed as a static title, Presenter is the right place:
But if this input data is about to be related to an use case, like to be part of an endpoint, there is nothing wrong to inject directly in our
This article brought you a better understanding about the different meanings Presenter and Interactor have in a VIPER architecture and when it's time to rely on each one. Basically you need to ask yourself if the operation you need operates with data entities, if it calls some external service or if it has some screen business logic. If at least one of these cases occur you need an Interactor. We also showed some of the most common examples of operations inside a scene and how to handle each one. I hope you are now more secure about building a new VIPER scene and ready to apply these skills in your personal projects ;)