Setting Up MVP Architecture for iOS: Template and Practice

Setting Up MVP Architecture for iOS: Template and Practice Imagine: your UIKit codebase has grown to 20+ screens, each ViewController carries 500+ lines, and tests are either absent or require launching the simulator. You tried MVVM, but the Combine bindings with `@Published` didn't fit—the proje

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
Setting Up MVP Architecture for iOS: Template and Practice
Medium
~2-3 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

Setting Up MVP Architecture for iOS: Template and Practice

Imagine: your UIKit codebase has grown to 20+ screens, each ViewController carries 500+ lines, and tests are either absent or require launching the simulator. You tried MVVM, but the Combine bindings with @Published didn't fit—the project is too old, or your team came from Android with its MVP culture. Our team has set up MVP on 15+ projects and guarantees the template works from the first commit. In this article, we'll cover key protocols, UIKit isolation, and testing without a simulator.

Wikipedia: Model-View-Presenter is an architectural pattern where the Presenter acts as an intermediary between View and Model. On iOS, this is especially useful for large UIKit projects where MVVM with Combine would require rewriting the code for a reactive stack.

Why MVP Fits UIKit Projects

If your project is not a simple list with details but a complex multi-screen UIKit app, MVP provides clear separation: the Presenter doesn't see UIKit, the ViewController is thinner, and tests are written without a simulator. Unlike MVVM where the ViewModel subscribes to Combine, MVP uses simple protocols and weak references. This lowers the entry barrier and doesn't require rewriting the code for a reactive stack. Such an approach is an element of clean architecture for iOS, as dependencies point from outer layers to the core.

What Is MVP on iOS and How It Differs from MVVM

Per the definition Model-View-Presenter is an architectural pattern where the Presenter acts as an intermediary between View and Model. In MVVM, the ViewController subscribes to @Published properties of the ViewModel via Combine. In MVP, the Presenter knows nothing about UIKit: it communicates with a View protocol implemented by the ViewController. No bindings, no reactivity—full control over the data flow.

// View protocol — interface for Presenter protocol ProfileView: AnyObject { func showUser(_ user: User) func showLoading(_ isLoading: Bool) func showError(_ message: String) } // Presenter — pure Swift, zero UIKit final class ProfilePresenter { weak var view: ProfileView? private let userRepository: UserRepository init(userRepository: UserRepository) { self.userRepository = userRepository } func viewDidLoad() { view?.showLoading(true) Task { do { let user = try await userRepository.fetchCurrentUser() await MainActor.run { view?.showLoading(false) view?.showUser(user) } } catch { await MainActor.run { view?.showLoading(false) view?.showError(error.localizedDescription) } } } } } // ViewController — thin, only UI final class ProfileViewController: UIViewController, ProfileView { private var presenter: ProfilePresenter! override func viewDidLoad() { super.viewDidLoad() presenter.viewDidLoad() } func showUser(_ user: User) { nameLabel.text = user.name avatarImageView.load(url: user.avatarURL) } func showLoading(_ isLoading: Bool) { isLoading ? activityIndicator.startAnimating() : activityIndicator.stopAnimating() } func showError(_ message: String) { // Toast or Alert } } 

Key point: weak var view: ProfileView?—a weak reference is mandatory, otherwise you get a retain cycle. The Presenter holds the View, the View holds the Presenter; one of them must be weak.

How to Test a Presenter Without a Simulator

The main advantage of MVP: the Presenter is testable without a simulator. Here's an example with a mock:

final class MockProfileView: ProfileView { var didShowUser = false var isLoading = false func showUser(_ user: User) { didShowUser = true } func showLoading(_ isOn: Bool) { isLoading = isOn } func showError(_ message: String) { /* ignore */ } } func testViewDidLoad_success() async { let mockView = MockProfileView() let mockRepository = MockUserRepository(result: .success(User.fixture)) let sut = ProfilePresenter(userRepository: mockRepository) sut.view = mockView sut.viewDidLoad() // Short pause for async Task try await Task.sleep(nanoseconds: 100_000_000) XCTAssertTrue(mockView.didShowUser) XCTAssertFalse(mockView.isLoading) } 

The test takes milliseconds, no XCUITest required. Code coverage with such tests reaches 80%, and regression budget savings up to 40%. Compare: a typical MVVM ViewModel with Combine requires setting up XCTestExpectation or Sink—MVP is significantly simpler and faster here.

What's Included in MVP Setup

When you order MVP setup, you get:

  • Complete Swift files for the module: Presenter, ViewController, Router.
  • Unit tests for the Presenter (5–10 cases per module, 80% coverage).
  • Integration instructions for adding new screens.
  • Team training (1–2 calls with a demo).

The work takes 2–3 days for a new project. Contact us to assess the volume of migrating existing screens. Get a consultation on implementing MVP in your project.

Navigation in MVP: What Is Router / Wireframe?

The Presenter should not manage navigation directly—that violates Single Responsibility. The classic solution is a Router (or Wireframe in original MVP terms):

protocol ProfileRouter: AnyObject { func navigateToEditProfile(user: User) func navigateToSettings() } 

A concrete ProfileRouterImpl works with UINavigationController—the UIKit dependency is isolated. The Presenter receives the Router via DI and calls router.navigateToEditProfile(user:) without knowing what happens under the hood.

How to Migrate from MVC to MVP

Migrating an existing MVC screen to MVP is a 4–8 hour task per screen. Create a View protocol, extract logic into a Presenter, add a Router. Move Model dependencies into the Presenter. Implement the View in the ViewController. Test the Presenter. This step-by-step plan applies to any screen, and the final architecture becomes modular and testable.

Step-by-Step MVP Setup for a New Project

  1. Define modules. Each screen is a separate module with View, Presenter, Router protocols.
  2. Write protocols. View—only display methods; Presenter—lifecycle and event handling methods; Router—navigation methods.
  3. Implement the Presenter. All logic, server/DB work. No UIKit.
  4. Implement the View (ViewController). Forward calls to the Presenter.
  5. Implement the Router. Create a class that uses UINavigationController for transitions.
  6. Set up DI. Use a module factory or Swinject to compose dependencies.
  7. Write tests. Cover every Presenter method (5–10 cases per module).
Step Duration
Define modules 1–2 h
Protocols & Presenter 3–4 h
View & Router 2–3 h
DI & factory 1–2 h
Tests (10 cases) 2–3 h
Integration 1 h

MVP vs MVVM Comparison

Characteristic MVP MVVM
UIKit dependency No (Presenter) No (ViewModel)
Binding None Combine (or Rx)
Testing without simulator Yes Yes (with caveats)
Complexity on SwiftUI Not applicable Natural
Stack in project UIKit UIKit / SwiftUI

Common MVP Implementation Mistakes

  • Forgetting weak on view—retain cycle, memory leak. Always use weak var view: ViewProtocol?.
  • Mixing navigation logic into the Presenter—extract a Router.
  • Not testing the Presenter—you lose the main advantage of MVP. Cover with mocks.
  • Overcomplicating protocols—one method per screen instead of grouping. Keep the number of View methods reasonable (5–7).

Order MVP setup for your project—get a ready-made template in 2–3 days. We guarantee your project will have a testable structure, and your team will get a workflow without unnecessary overhead. Contact us for a consultation.