Author
Maximiliano Casal
Every time we start a fresh project, we are compelled to choose from many patterns, for us to be able to ultimately architect our solution in a way that it primarily stays maintainable and scalable. However, we may feel stunned because of the number of options and variations. By the time are done with this post, you will hopefully have gained the necessary knowledge for you to drill even further to implement two of the most common (and known) patterns: Model View Controller -MVC- and Model View Presenter -MVP-.
To learn the key concepts, I decided to include a Pokedex starter project which implements the MVC pattern. The idea is that you make your own edits to the project and, by the end of the tutorial, you will have the same Pokedex –but following a MVP approach–, while gaining some experience on how to migrate MVC apps to MVP!
I will first walk you through a quick overview of MVC and MVP, and then compare them against each other.
MVC & MVP Architecture
MVC defines three roles: the controller, the view and the model; however, this pattern not only describes the roles, it also outlines the interactions between the components. The Controller is only a mediator between the View and the Model, preventing them from “knowing” each other. If you are looking for a more exhaustive explanation, you may want to take a look at Apple’s MVC definition.
In theory, it looks very straightforward, but in reality, it does not. The number of Controllers tend to grow significantly, when you suddenly realise that your script counts more than 1,000 lines, or even more! That’s why you may hear people referring to MVC as the “Massive View Controller”. This title results from their tight dependency with the View’s life cycle, making it hard to treat them as separate entities. In the end, the View Controller turns out to be responsible for everything.
On the other hand we have the MVP pattern that is an “evolution” of MVC. In here we also deal with three components: the Presenter (UIKit independent mediator), the Passive View (UIView and/or UIViewController) and the Model. This pattern defines Views as recipients of the UI events, which then call the appropriate Presenter as needed. The Presenter is, in fact, responsible for updating the View with the new data returned by the Model.
Migration Tutorial
At this point you should have gotten a fairly decent idea of what both the MVC & MVP patterns entail, so let’s move on now with our brief tutorial.
1. First run
The first thing you have to do is to download the Starter project from Pokedex MVC. Then you need to build and run the project. You should see something like this:
2. Creating the files needed
Now let’s go ahead and migrate our app’s architecture from MVC to MVP. First of all, you have to create the Views and the Presenters. Create these files: PokemonListView, ItemsListView, PokemonPresenter & ItemsPresenter. Now your project should look like this:
3. Creating the Views
Now let’s begin with the Views. The idea is that you need to define the methods that the UIViewController implements in order to display the information. A good practice is to first think about the goal of the UIViewController, and the method(s) it probably needs implement to meet such goal.
For example, in the case of the PokemonListViewController, the main purpose is to render a list of existing Pokemons and be able to search for a given one. Also, as we fetch the data from the API, we should have a visual aid depicting that the request is still processing, and, at the same time, prompt a message in case the request fails for any reason.
PokeListView code
import Foundation protocol PokeListView { func addPokemon(pokemon: Pokemon) func getInitialPokemons() func showLoadingIndicator() func hideLoadingIndicator() }
So, as a recap, you should first think about the main goal of the PokeItemsView, and then implement the class.
4. Creating the Presenters
The Presenters shall handle the calls to the APIManager to pull the information from the services.
import Foundation class PokeListPresenter { private var pokemonView : PokeListView? init() { } func attachView(view: PokeListView) { pokemonView = view } func detachView() { pokemonView = nil } func getInitialPokemons() { for id in 1...5 { APIManager.sharedInstance.retrivePokemon(id, completionHandler: { (pokemon) in self.pokemonView?.addPokemon(pokemon) }) } } func getNextPokemons(range: Int) { for id in range...range+5 { APIManager.sharedInstance.retrivePokemon(id, completionHandler: { (pokemon) in self.pokemonView?.addPokemon(pokemon) }) } } }
Please ensure you create the PokeItemsPresenter!
5. Using the Presenters in the UIViewControllers
The first thing you have to do is to extend the ViewController to implement the View (e.g. our PokeListViewController implements PokeListView).
extension PokeListViewController : PokeListView { func getInitialPokemons() { presenter.getInitialPokemons() } func addPokemon(pokemon: Pokemon) { lastPokemon = pokemon.id! pokemons.append(pokemon) pokemonTableView.reloadData() } private func getNextPokemons() { if pokemons.count == lastPokemon { let range = lastPokemon+1 presenter.getNextPokemons(range) } } }
Once you have built that PokeListView protocol, you then need to create a PokeListPresenter class in your PokeListViewController.
It should look like this:
class PokeListViewController: UIViewController { @IBOutlet var loadingView: UIView! @IBOutlet var pokemonTableView: UITableView! private let kPokeCellIdentifier = "kPokeCellIdentifier" private var pokemons = [Pokemon]() private let searchController = UISearchController(searchResultsController: nil) private var filteredPokemons = [Pokemon]() private var lastPokemon = 1 private var presenter = PokeListPresenter() override func viewDidLoad() { super.viewDidLoad() setupSearchController() presenter.attachView(self) presenter.getInitialPokemons() } }
6. Final run
Final project
Now, be proud of yourself! It’s there now!! Congratulations!
If you feel like downloading our solution and compare against your own version, please feel free to download ours from Pokedex MVP. And if you want, you can also read all about our Software Development Firm.