Architectural Pattern - Model–view–presenter (MVP)
Binoy Vijayan
Posted on February 6, 2024
Model–view–presenter (MVP) is a derivation of the model–view–controller (MVC) architectural pattern which mostly used for building user interfaces. In MVP, the presenter assumes the functionality of the “middle-man”. In MVP, all presentation logic is pushed to the presenter. MVP advocates separating business and persistence logic out of the view
Differences between MVC and MVP
Model View Controller
Controllers are behaviour based and can share multiple views.
View can communicate directly with Model
Model View Presenter
View more separated from Model. The Presenter is the mediator between Model and View.
Easier to create unit tests
Generally there is a one to one mapping between View and Presenter, with the possibility to use multiple Presenters for complex Views
Listen to user action and model updates
Updates model and view as well
Model
In an application with a good layered architecture, this model would only be the gateway to the domain layer or business logic. See it as the provider of the data we want to display in the view. Model’s responsibilities include using APIs, caching data, managing databases and so on.
View
The View, will contain a reference to the presenter. The only thing that the view will do is to call a method from the Presenter every time there is an interface action.
Presenter
The Presenter is responsible to act as the middle man between View and Model. It retrieves data from the Model and returns it formatted to the View. But unlike the typical MVC, it also decides what happens when you interact with the View.
Here's a simple example of how you can implement the MVP pattern in Swift. In this example, we'll create a basic login screen:
Model
import UIKit
// Model
struct User {
let username: String
let password: String
}
Presenter
protocol LoginPresenterProtocol {
func loginButtonTapped(username: String?, password: String?)
}
class LoginPresenter: LoginPresenterProtocol {
weak var view: LoginViewProtocol?
func loginButtonTapped(username: String?, password: String?) {
guard let username = username, let password = password else {
view?.showError(message: "Please enter both username and password.")
return
}
// Here you can add your business logic, like authentication
let user = User(username: username, password: password)
view?.loginSuccessful(user: user)
}
}
View
protocol LoginViewProtocol: AnyObject {
func showError(message: String)
func loginSuccessful(user: User)
}
class LoginViewController: UIViewController, LoginViewProtocol {
@IBOutlet weak var usernameTextField: UITextField!
@IBOutlet weak var passwordTextField: UITextField!
var presenter: LoginPresenterProtocol!
override func viewDidLoad() {
super.viewDidLoad()
presenter = LoginPresenter()
presenter.view = self
}
@IBAction func loginButtonTapped(_ sender: UIButton) {
presenter.loginButtonTapped(username: usernameTextField.text, password: passwordTextField.text)
}
func showError(message: String) {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
func loginSuccessful(user: User) {
let message = "Welcome, \(user.username)!"
let alert = UIAlertController(title: "Success", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
present(alert, animated: true, completion: nil)
}
}
In this example:
User represents the model containing user data.
LoginPresenter acts as the presenter, which mediates between the view and the model. It contains the business logic for the login functionality.
LoginViewController is the view, which is a UIViewController. It contains the UI elements for the login screen and handles user interactions. It also conforms to the LoginViewProtocol, allowing the presenter to communicate with it.
The presenter is injected into the view controller, establishing the connection between them.
When the login button is tapped, the view controller calls the loginButtonTapped method on the presenter, passing the username and password entered by the user.
Depending on the result of the login attempt, the presenter notifies the view either with an error message or a successful login message.
Here are some advantages and disadvantages of using the MVP (Model-View-Presenter) architectural pattern in software development:
Advantages:
Separation of Concerns: MVP promotes a clear separation of concerns by dividing the user interface (View) from the application logic (Presenter) and the data/model layer. This separation enhances maintainability and testability.
Testability: MVP makes it easier to unit test the application components. Since the Presenter contains most of the application logic, it can be tested independently of the View, which often requires complex mocking in other architectures like MVC.
Flexibility and Scalability: MVP allows for greater flexibility and scalability in development. With well-defined interfaces between the View, Presenter, and Model, it's easier to make changes to one component without affecting the others, making the application more adaptable to changes over time.
Reduced Dependency: The View is kept as passive as possible in MVP, with minimal logic. This reduces the dependency between the presentation logic and the UI framework, making it easier to switch UI frameworks or adapt to different platforms.
Improved Maintainability: By separating the concerns and enforcing clear boundaries between components, MVP leads to code that is easier to understand, debug, and maintain over time. This is especially beneficial for larger projects with multiple developers.
Disadvantages:
Complexity: Implementing MVP can introduce additional complexity compared to simpler architectures like MVC. Developers need to carefully manage communication between the View, Presenter, and Model, which may lead to more boilerplate code and increased development time.
Learning Curve: MVP may have a steeper learning curve for developers who are not familiar with the pattern. Understanding the responsibilities of each component and how they interact with each other requires some initial effort and may pose a challenge for beginners.
Potential Overhead: In some cases, MVP can introduce additional overhead, especially for smaller projects or simple user interfaces where the benefits of the pattern may not outweigh the added complexity. It's important to weigh the benefits against the costs when deciding whether to adopt MVP for a particular project.
Tighter Coupling Between Presenter and View: While MVP aims to keep the View passive, there can still be tight coupling between the Presenter and the View, especially when dealing with UI-related tasks such as updating UI elements. This can make it harder to achieve true separation of concerns in practice.
Increased File Count: MVP often requires creating more files/classes compared to simpler architectures like MVC, which may lead to a larger codebase. This can make navigation and code organisation more challenging, especially for larger projects.
Posted on February 6, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.