VIPER — Develop and Test -Part I

Mushtaque Ahmed
7 min readSep 20, 2019

--

Mobile architecture is one of the most ignored field in mobile application domain. When we hear the word Architect we mostly think about Server side architect who design the micro services, suggest performance enhancement techniques and help make scalable application. Many organizations don’t even have a Mobile architect who could lead the development team to create a scalable, robust performant applications.

But with mobile applications reaching to millions of customers now, scaling to millions of lines of code, being feature pumped, constantly being changed and released to adapt to user requirements and being the face of the company, the technical team is realizing the importance of an mobile architect to design the mobile application from bottom up since the beginning of the development cycle. Mobility Domain is an industry in itself now and is going to stay. So its necessary for every mobile application development team to have an architect who understand the mobile architecture, OS and can help design the application for different platforms (iOS or Android).

Here in this post I will be discussing one of the latest Design Patterns which helps and enhances a mobile application architecture to fit the SOLID Principles and helps in better unit testing of the codes : VIPER.

Assuming that most of you have already know or have faced the issue of Massive View Controller which is a very common mistake that mobile developers make, and have tried different design pattern like MVP or MVVM to deal with the issue, I wont go in details with these two patterns. Lets talk about next generation design Pattern VIPER.

VIPER is build over MVP/MVVM where it divides the responsibility to further granule level to fit the SOLID Principles. One of the feature of SOLID is to achieve single responsibility at the architectural level. Which not only improves code readability and scalability but also improves Unit testing of the code. We will analyse and understand VIPER with a small use case of Login functionality of an app, how to divide the VC logic from business logic, where to make network calls and call back, where the model/entity will be placed and used and how to route the application.

VIPER consist of 5 different layers

  1. View
  2. Presenter
  3. Interactor
  4. Entity / Model
  5. Router

View: The view class (LoginViewController.swift ) holds the view components i.e IBOutlets , View Life cycle methods and UI actions methods. This files keeps all the view logic. You should try to avoid any business logic or make network calls from here.

Presenter: is where the view model is placed. All the datas relating to views are defined in a view presenter. Presenter is the parent class which keeps ownership of the router , and interactor.

Router: is responsible for the navigation of the application. All the storyboard identity , different view controllers to be shown on any event are placed in it.

Interactor: is responsible to make any network calls to backend services and notify the presenter on the availability of data. It owns a network manager and local data base class to store data locally.

Entity: This holds the footprint of how the response or any data should be translated in. Its basically like the models we create to store data in our user defined data structure or Core Data.

The architecture of VIPER Pattern

I will explain each layer with a simple Login example project with code. I have created a small git project https://github.com/mushtaque87/VIPER_IOS which consist of a
1. ViewController (LoginViewController.swift : A Login Page as shown )
2. Presenter class (LoginPresenter.swift) ,
3.Router file/class(LoginRouter.swift) ,
4.Interactor class (LoginInteractor.swift)
5.Model/entity (LoginModel.swift).

The detail explanation will be followed by a class diagram to show you how each and every class are connected, communicate and data flow through each classes. The project just shows you a simple login screen where it makes a network call** and receive response and updates the UI.
** Here we will not make a network call but read response data from a text file in the project bundle as I dont’ have any login service hosted.

This is the class diagram for a Massive View Controller

As most of us have faced or made mistake of Massive View Controller where we put the whole application , business , view logic at one ViewController Class and even make network calls from it (as its easy that way :P). But when the scale of the application increases not only the readability of the code diminishes but we also land up spending huge amount time to fix even simple bugs as it becomes difficult to identify and debug the effected area.

This is the class diagram for a View Controller (Massive) for just a simple login screen in our app.

Lets convert this Login module into VIPER Pattern.

We can first create and initiate a Presenter class (LoginPresenter) and provides to the VC (LoginViewController) .

final class LoginViewController: UIViewController {private var presenter: LoginPresenter!override func viewDidLoad() {
presenter = LoginPresenter(interactor: LoginInteractor(loginService: ServiceManager()), router: LoginRouter()))
}

VIPER is a delegate driven pattern, so the communication between different layers like Presenter and Interactor happens over delegates.

The presenter owns interactor class (LoginInteractor) which conforms to LoginInteractorProtocol. In order to ask interactor to perform any network relating calls, Presenter call the protocol method which the LoginInteractor conforms to and Presenter listens to it for translated response.

final class LoginPresenter {
private var interactor: LoginInteractorProtocol
init(interactor: LoginInteractorProtocol) {
self.interactor = interactor
}
func login(with userName: String,
password: String,
onSuccess:@escaping (Bool) -> Void,
onFailure: @escaping () -> Void) {
self.interactor.login(with: userName, password: password, onSuccess: { (success) in
if(logindetails.username == userName ) {
onSuccess(true)
}
else {
onSuccess(false)
}
}) {
onFailure()
}
}
}

The interactor owns a ServiceManager(ServiceManager.swift) which makes actual request and handle responses. As mentioned above interactor conforms to a protocol and implements its methods which the presenter has called and is waiting for the response.

protocol LoginInteractorProtocol {func login(with userName: String,
password: String,
onSuccess:@escaping (LoginModel) -> Void,
onFailure: @escaping () -> Void)
}
final class LoginInteractor: LoginInteractorProtocol {private var loginService: LoginServiceProtocol
init(loginService: LoginServiceProtocol) {
self.loginService = loginService
}
func login(with userName: String, password: String, onSuccess: @escaping (LoginModel) -> (), onFailure: @escaping () -> ()) {self.loginService.login(with: userName, password: password, onSuccess: { (logindetails) in
onSuccess(logindetails)
}) {
onFailure()
}
}
}

Once the response comes the interactor uses the model/entity to translate the response in desired model and send back the translated response to the presenter.

struct LoginModel : Codable {
let accesstoken : String?
let uid : String?
let refreshtoken : String?
let username : String?
enum CodingKeys: String, CodingKey {
case accesstoken = "accesstoken"
case uid = "uid"
case refreshtoken = "refreshtoken"
case username = "username"
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
accesstoken = try values.decodeIfPresent(String.self, forKey: .accesstoken)uid = try values.decodeIfPresent(String.self, forKey: .uid)refreshtoken = try values.decodeIfPresent(String.self, forKey: .refreshtoken)username = try values.decodeIfPresent(String.self, forKey: .username) }
}

Once the network calls are completed successfully, based on the status of the call the ViewController decides which screen to show next with the help of the Router

//If the username is same as in the response(LoginResponse.txt) the Profile screen is shown or the app is routed to the signUp screen as it is a new user.private func startLoginProcess(with userName: String, password: String) {
presenter.login(with: userName, password: password, onSuccess: {[weak self] (isSuccess) in
if(isSuccess) {
print("Login Passed")
self?.showProfileScreen()
} else {
print("Login Failed")
self?.showSignUpScreen()
}
}) {
print("Login Failed")
self.showSignUpScreen()
}
}
func showSignUpScreen() {
let signUpVC = LoginRouter.getSignUpScreen()
self.navigationController?.pushViewController(signUpVC, animated: true)
}
func showProfileScreen() {
let profileVC = LoginRouter.getProfileScreen()
self.navigationController?.pushViewController(profileVC, animated: true)
}
class LoginRouter {class func getSignUpScreen() -> SignUpViewController {
let signUpViewController = mainStoryboard.instantiateViewController(withIdentifier: "SignUpViewController") as? SignUpViewControllerreturn signUpViewController!
}
class func getProfileScreen() -> ProfileViewController {
let profileViewController = mainStoryboard.instantiateViewController(withIdentifier: "ProfileViewController") as? ProfileViewController
return profileViewController! }
}

Using VIPER we can resolve the Massive View Controller issue. Following is the class diagram for the whole flow. Notice how the View controller only holds methods and outlets regarding to view and the responsibility of calling the network is moved to Interactor and Router routes the app to different screens with Presentation layer being the middle layer holding every layer together.

Download the github project and run it to analyse the code to check the flow and code abstraction. This is a clean way of writing code and easy to identify the areas effected by bugs. In the git project you can find a similar SignUp and Profile module like the Login Module which uses similar VIPER architecture to handle there views and data flow as above.

Next I will release PART-II of this article explaining how easy it is to Test the Login module using VIPER Architecture.

Happy Coding !!!

--

--

Mushtaque Ahmed

Mobile Architect | iOS | Swift | RxSwift | GRPC | VIPER | | Protocol Oriented Programming | Realm | cocoapods | Appium | HTML | React.js | React Native