Skip to content
Ryan Bush edited this page Apr 21, 2017 · 4 revisions

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.

Being a Reactive View

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!

Communicating with a Presenter

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.

Communicating with the Presenter

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()
}

Communicating with a DataStore

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.