Getting Started with the VIPER Architecture Pattern for iOS Application Development (2024)

Marwan Ayman

Posted on • Updated on

Getting Started with the VIPER Architecture Pattern for iOS Application Development (2) Getting Started with the VIPER Architecture Pattern for iOS Application Development (3) Getting Started with the VIPER Architecture Pattern for iOS Application Development (4) Getting Started with the VIPER Architecture Pattern for iOS Application Development (5) Getting Started with the VIPER Architecture Pattern for iOS Application Development (6)

#ios #swift #viper #mobile

When you are planning to build an app, one of the most important decisions is to choose how to structure your app’s core.

Better saying, you need to decide which architecture you should adopt to build all your screens, features, and contexts. You need to know how to organize your app in order to make it maintainable for the long term, testable, scalable, and understandable by anyone who enters the project sometime later. In this article, we will get familiar with a design pattern called VIPER (View, Interactor, Presenter, Entity, and Router.) for iOS development.

What is VIPER?

VIPER is an architectural pattern like MVC or MVVM, but it separates the code further by single responsibility.
One feature, one module. For each module, VIPER has five different classes with distinct roles. No class goes beyond its sole purpose.

Each of the letters in VIPER stand for a component of the architecture: View, Interactor, Presenter, Entity and Router.

Getting Started with the VIPER Architecture Pattern for iOS Application Development (7)

Below I will explain an example of VIPER

Protocols
I have created a separate file for all the protocols

protocol NewsListView: AnyObject {}protocol NewsListPresenter: AnyObject { func viewDidLoad(view: NewsListView)}protocol NewsListInteractorInput: AnyObject {}protocol NewsListInteractorOutput: AnyObject {}protocol NewsListRouter: AnyObject {}protocol NewsListRepo: AnyObject {}

As you can see from the above code, this is the main contract agreement between VIPER layers.

Presenter

PresenterImplementation is an implementation of NewsListPresenter protocol and confirm the NewsListInteractorOutput.

In this layer presenter has a reference object from View, Router and Interactor

final class NewsListPresenterImplementation: NewsListPresenter{ private weak var view: NewsListView? private let router: NewsListRouter private let interactor: NewsListInteractorInput init(router: NewsListRouter, interactor: NewsListInteractorInput) { self.router = router self.interactor = interactor } func viewDidLoad(view: NewsListView) { self.view = view }}extension NewsListPresenterImplementation: NewsListInteractorOutput {}

Interactor
Interactor is an implementation of NewsListInteractorInput protocol

NewsListRepo is responsible to fetch the data from network or Data provider

final class NewsListInteractor: NewsListInteractorInput { weak var output: NewsListInteractorOutput? private let repo: NewsListRepo init(repo: NewsListRepo) { self.repo = repo }}

View
View is a UIViewController with a confirmation of NewsListView protocol and it has reference to the presenter

final class NewsListViewController: UIViewController, NewsListView { private let presenter: NewsListPresenter init(presenter: NewsListPresenter) { self.presenter = presenter super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() presenter.viewDidLoad(view: self) }}

Entity

struct News { let title: String let url: URL}

Router

final class NewsListRouterImplementation: NewsListRouter { weak var viewController: UIViewController?}

Now we need to update our VIPER module to fetch the news data, yes as you are thinking right now, we need to update our contract protocols first

protocol NewsListView: AnyObject { func show(newsList: [News])}protocol NewsListPresenter: AnyObject { func viewDidLoad(view: NewsListView)}protocol NewsListInteractorInput: AnyObject { func fetchNewsList()}protocol NewsListInteractorOutput: AnyObject { func fetchNewsListSuccess(newsList: [News]) func fetchNewsListFailure(error: Error?)}protocol NewsListRepo: AnyObject { func fetchNewsList(completion: @escaping ([News]?, Error?) -> Void)}protocol NewsListRouter: AnyObject {}

Let’s now add implementation for theses added funcs in the protocols

Interactor

final class NewsListRepoImplementationl: NewsListRepo { func fetchNewsList(completion: @escaping ([News]?, Error?) -> Void) { // Fetch the data with completion }}

final class NewsListInteractor: NewsListInteractorInput { weak var output: NewsListInteractorOutput? private let repo: NewsListRepo init(repo: NewsListRepo) { self.repo = repo } func fetchNewsList() { repo.fetchNewsList { news, error in if let newsList = news { self.output?.fetchNewsListSuccess(newsList: newsList) } else { self.output?.fetchNewsListFailure(error: error) } } }}

Presenter

final class NewsListPresenterImplementation: NewsListPresenter{ private weak var view: NewsListView? private let router: NewsListRouter private let interactor: NewsListInteractorInput init(router: NewsListRouter, interactor: NewsListInteractorInput) { self.router = router self.interactor = interactor } func viewDidLoad(view: NewsListView) { self.view = view // Fetch the list from the interactor interactor.fetchNewsList() }}extension NewsListPresenterImplementation: NewsListInteractorOutput { func fetchNewsListSuccess(newsList: [News]) { self.view?.show(newsList: newsList) } func fetchNewsListFailure(error: Error?) { // show Error }}

View

final class NewsListViewController: UIViewController, NewsListView { private let presenter: NewsListPresenter init(presenter: NewsListPresenter) { self.presenter = presenter super.init(nibName: nil, bundle: nil) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func viewDidLoad() { super.viewDidLoad() presenter.viewDidLoad(view: self) } func show(newsList: [News]) { // show the news in the UI }}

Now, we are fetching the news list from the interactor layer, then update the presenter with the result and the presenter is prepare the data for the View and boom pass it

Last piece from the puzzle here is to build our module, so I have created a builder class to launch my module

final class NewsListBuilder { func build() -> UIViewController { let router = NewsListRouterImplementation() let repo = NewsListRepoImplementation() let interactor = NewsListInteractor(repo: repo) let presenter = NewsListPresenterImplementation(router: router, interactor: interactor) let view = NewsListViewController(presenter: presenter) router.viewController = view interactor.output = presenter return view }}

Conclusion

VIPER is sounds complex in the beginning but it’s very clean architecture. It isolates each module from others. So changing or fixing bugs is very easy as you only have to update a specific module. Also for having a modular approach VIPER creates a very good environment for unit testing. As each module is independent of others, it maintains low coupling very well. So, dividing work among co-developers is also pretty simple.

My advice if you are going to using VIPER in your project, the smartest thing would be to use an automatic module structure generator. Otherwise creating files for modules will be a big hustle, There are few generators available online.

VIPER Gen
VIPER Code

Thank you for reading! If you liked this article, please Like so other people can read it too :)

Happy coding ✌️

Reach me out here

Getting Started with the VIPER Architecture Pattern for iOS Application Development (2024)
Top Articles
Latest Posts
Article information

Author: Nicola Considine CPA

Last Updated:

Views: 5911

Rating: 4.9 / 5 (49 voted)

Reviews: 88% of readers found this page helpful

Author information

Name: Nicola Considine CPA

Birthday: 1993-02-26

Address: 3809 Clinton Inlet, East Aleisha, UT 46318-2392

Phone: +2681424145499

Job: Government Technician

Hobby: Calligraphy, Lego building, Worldbuilding, Shooting, Bird watching, Shopping, Cooking

Introduction: My name is Nicola Considine CPA, I am a determined, witty, powerful, brainy, open, smiling, proud person who loves writing and wants to share my knowledge and understanding with you.