-
Notifications
You must be signed in to change notification settings - Fork 9
View
An View
is responsible for the user interface. It is the layer that retrieves information and events from the user and relates that to the Presenter.
It has outlets only to the Presenter of the VIPER stack, something like this:
weak var presenter: ViewToPresenterInterface!
It is important to understand that the View
is dumb, it does not drive interactions of any kind. This is typically a very difficult concept for people new to VIPER, as with MVC, we are used to responding to View
events like viewDidLoad
or viewDidAppear
. In VIPER, these events are handled by the Presenter, and the Presenter is what tells the View
what to do.
The View
in a VIPER stack is reactive, not proactive. It only updates the UI in response to a command from the Presenter. This is important to understand as this is what causes the UI to be independent of data flow and easily changed. Lets say there is a jogging application, and the module has been told to present a screen that shows all the users jogging sessions. Somehow, the Presenter is told that some jogs were fetched.
//Presenter.swift
func fetchedJogs(jogs: [Jog]) {
view.display(jogs: jogs)
}
//View.swift
func display(jogs newJogs: [Jog]) {
jogs = newJogs
tableView.reloadData()
}
When the Presenter receives jogs in someway, it then knows it needs to tell the View
to display them, so it calls the display(jogs:)
method on the View
. This particular View
uses a UITableView
to display the jogs, so it just saves the jogs and tells the tableView
to reload its data.
What if you wanted to change this implementation to use a UICollectionView
? The display(jogs:)
function would stay the same, and the Presenter/Interactor/Wireframe would never need to be touched. You could create a new View
object that conforms to the same PresenterToViewInterface
, but this one uses a UICollectionView
implementation. Then this new View
is just dropped into the place and you're all done!
Ok, now lets say your user wants to login to the application, so the View
is displaying two text fields, one for username entry, and the other for password. The user types in their username and password, then presses a Login
button.
//View.swift
@IBAction func loginTapped(sender: AnyObject) {
let username = usernameTextField.text
let password = passwordTextField.text
presenter.userTappedLogin(withUsername: username, andPassword: password)
}
//Presenter.swift
func userTappedLogin(withUsername username: String, andPassword password: String) {
interactor.login(withUsername: username, andPassword: password)
}
Here, the View
tells the Presenter of the user event, and communicates the information that it gathered (username and password). The Presenter then decides what to do with the user event. Notice this flow isn't initiating a login call to the backend. It is just notifying the Presenter of the user event.
So the loginService
succeeded, and the success block of the Service is ran, and now this information needs to be conveyed to the Presenter.
//Interactor.swift
func loggedIn(withUser: user) {
presenter.logginSucceeded()
}
So what if instead of calling a Service, you instead want to get information from some sort of data manager like Realm? Lets say we are making a jogging application that records the user's jogging sessions.
//Presenter.swift
func beganPresenting() {
interactor.fetchJogs()
}
//Interactor.swift
func fetchJogs() {
let realm = try! Realm()
let allJogs = realm.objects(Jog.self)
presenter.fetchedJogs(Array(allJogs))
}
Notice the interface to the Interactor
from the Presenter is the same as if the Interactor
was going to call a Service. The Presenter has no idea how the Interactor
fetches jogs (or logs in the user). The Interactor
is responsible for this interaction.