VIPER Design Pattern
What Is Viper
Viper Design pattern is part of design architecture design patterns.It is application of clean architecture in iOS.It is based on “Single Responsibility Principle”. The word VIPER is a backronym for View, Interactor, Presenter, Entity, and Routing. Clean Architecture divides an app’s logical structure into distinct layers of responsibility. This makes it easier to isolate dependencies (e.g. your database) and to test the interactions at the boundaries between layers: Insert image of VIPER
Part of VIPER
- View: The responsibility of the view is to send the user actions to the presenter and shows whatever the presenter tells it.
- Interactor: This is the backbone of an application as it contains the business logic.
- Presenter: Its responsibility is to get the data from the interactor on user actions and after getting data from the interactor, it sends it to the view to show it. It also asks the router/wireframe for navigation.
- Entity: It contains basic model objects used by the Interactor.
- Router: It has all navigation logic for describing which screens are to be shown when. It is normally written as a wireframe. We use another class by name “Protocols” where we put all protocol needed for communications
Why We use Viper
Viper has many benefits . Because it supports “ Single Responsibility principle ” and clean architecture like
Testability
The loosely coupled modules ensure that testing is carried out easily and separately.
Easy to iterate on
The codebase is familiar to most developers. Files are smaller, logic is clearer and the overall stability and flexibility are higher.
VIPER is a step forward to ensure that the SOLID code principles can be further applied to iOS apps as well. The beauty of this architecture is that it stays true to the theory behind its implementation.
Example of Viper
Create project by using Single View application Create required classes for use
- Protocol class - It will contain all protocol for communication between components
- Entity class - It wil contains basic model object class
- View Controller Class - this class is responsible for handling UI components
- Interactor - This class is most important . It handles all business logic of app.
- Presentor - this class is resposnible for communication/mediator between Interactor and view
- Router - Router class is responsible for handling screen shown and basic initialization of components
Open Project protocol class and initialize 5 protocol we use for communication
protocol PresentorToViewProtocol : class {
}
protocol InteractorToPresentorProtocol : class {
}
protocol PresentorToInteractorProtocol : class {
}
protocol ViewToPresentroProtocol : class {
}
protocol PresentorToRouteProtocol : class {
}
Now time to create our simple view. I am using a tableView and Button to fetch some data from free online Json Placeholder Site .
(See project for View)
Time to implement functionality for fetching data from server. I am using Json Place holder api. Free open source rest api
Make entity modeler fetching data. This API provides list of TODO list . So make entity file by “TodoObject” .for fetching data , we will use Alamofire .install the pod of Alamofire by
pod 'Alamofire'
We need to get data by Rest API on “Get data button” click. Make method in “ViewToPresentroProtocol” to ask from presenter about new Todo List. In ViewToPresentroProtocol class write
func getTodoList()
Open the presenter class that implements “ViewToPresentroProtocol” and implement this function. Now presentor will call Interactor for business logic/ call rest API. Because presentor implements ViewToPresentroProtocol so make interactor object in protocol and use here. Write below line in ViewToPresentroProtocol
var interactor: PresentorToInteractorProtocol?
Implement business logic in Interactor. Interaction will implement “PresentorToInteractorProtocol” Make function in that protocol so that presentor can talk with it. In PresentorToInteractorProtocol protocol make
func fetchTodoList()
And implement in Interactor
After fetching Todo list, interactor will send update to presentor , So get object of presentor protcol. In *PresentorToInteractorProtocol make presentor protocol object for delegate
var presenter: InteractorToPresentorProtocol? {get set}
Now implement business logic of getting Todo List from server. I am using Alamofire. For code , After getting result we need to send back to presentor to inform that we have got result. Make 2 delegate function in “InteractorToPresentorProtocol”
func todoListFetched(todoList: [TodoObject])
func todoListFetchedFailed()
Interactor code should look like this
func fetchTodoList() {
Alamofire.request(URL).response { response in
if(response.response?.statusCode == 200){
guard let data = response.data else { return }
do {
let decoder = JSONDecoder()
let newsResponse = try decoder.decode([TodoObject].self, from: data)
self.presenter?.todoListFetched(todoList: newsResponse)
} catch let error {
print(error)
}
}
else {
self.presenter?.todoListFetchedFailed()
}
}
}
Now implement Interactor to presentor protocol in Presentor class and implement that methods to get response. Meanwhile in getTodoList() of presentor class , call interactor?.fetchTodoList() for getting data from Interactor
Everything is looking fine so far without any visualtion. Now time to initialize router that will handle the routing and initialing
In project router implement “PresentorToRouteProtocol” Create module for initialization in this protocol. It will be static because router is shared among all components
static func createModule(forViewController view: UIViewController )
Initialize all components in this function . It will look like this
static func createModule(forViewController view: UIViewController) {
let presenter: ViewToPresentroProtocol & InteractorToPresentorProtocol = ProjectPresentor()
let interactor: PresentorToInteractorProtocol = ProjectInteractor()
let router: PresentorToRouteProtocol = ProjectRouter()
(view as! ViewController).presenter = presenter
presenter.view = view as? PresentorToViewProtocol
presenter.router = router
presenter.interactor = interactor
interactor.presenter = presenter
}
If (view as! ViewController).presenter = presenter makes error .. initialize presentor in ViewController class
var presenter: ViewToPresentroProtocol?
Now create module in ViewDidLoad of controller
override func viewDidLoad() {
super.viewDidLoad()
ProjectRouter.createModule(forViewController: self)
}
It will initialize all components for eachother. On GetData button click, call presentor for todo list
After debugging , we have got result from API. And passed too presentor . Now pass result from presenter to View . Create these 2 functions in “PresentorToViewProtocol” for handling results.
func showTodoList(todoList: [TodoObject])
func showError()
In presentor , where implementing the InteractorToPresentorProtocol, send back the fetched result to view
func todoListFetched(todoList: [TodoObject]) {
view?.showTodoList(todoList: todoList)
}
func todoListFetchedFailed() {
view?.showError()
}
View controller will implement “PresentorToViewProtocol” . Add functionality in delegates method like show error pop up on error
extension ViewController : PresentorToViewProtocol {
func showTodoList(todoList: [TodoObject]) {
}
func showError() {
let alert = UIAlertController(title: "Alert", message: "Problem Fetching Todo list", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Okay", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
Get the result in func showTodoList(todoList: [TodoObject]) and show on tableView
Done! . You can download demo project for help