Настройка архитектуры MVP для iOS-приложения

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1735 услуг
Настройка архитектуры MVP для iOS-приложения
Средний
~2-3 дня
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    495

Настройка архитектуры MVP для iOS-приложения

MVP на iOS вышел из моды с приходом SwiftUI и MVVM+Combine, но на UIKit-проектах с большой кодовой базой он по-прежнему оправдан. Особенно там, где команда пришла из Android (где MVP был стандартом до Jetpack) или где исторически сложилась MVP-структура, которую незачем ломать ради тренда.

Чем MVP отличается от MVVM на UIKit

В MVVM ViewController подписывается на @Published-свойства ViewModel через Combine. В MVP — Presenter не знает о UIKit: он работает с протоколом View, а ViewController реализует этот протокол и сам обновляет UI. Нет биндингов, нет реактивности — зато полная контролируемость потока данных.

// View protocol — интерфейс для Presenter
protocol ProfileView: AnyObject {
    func showUser(_ user: User)
    func showLoading(_ isLoading: Bool)
    func showError(_ message: String)
}

// Presenter — чистый Swift, ноль 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 — тонкий, только UI
final class ProfileViewController: UIViewController, ProfileView {
    private var presenter: ProfilePresenter!

    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 или Alert
    }
}

Ключевой момент: weak var view: ProfileView? — слабая ссылка обязательна, иначе retain cycle. Presenter держит View, View держит Presenter — один из них должен быть weak.

Тестирование Presenter

Главное преимущество MVP — Presenter тестируется без симулятора:

func testViewDidLoad_success() async {
    let mockView = MockProfileView()
    let mockRepository = MockUserRepository(result: .success(User.fixture))
    let sut = ProfilePresenter(userRepository: mockRepository)
    sut.view = mockView

    sut.viewDidLoad()
    // Небольшая пауза для async Task
    try await Task.sleep(nanoseconds: 100_000_000)

    XCTAssertTrue(mockView.didShowUser)
    XCTAssertFalse(mockView.isLoading)
}

MockProfileView реализует ProfileView с флагами вызовов. Тест занимает миллисекунды, не требует XCUITest.

Навигация в MVP: Router / Wireframe

Presenter не должен управлять навигацией напрямую — это нарушает Single Responsibility. Классическое решение: Router (или Wireframe в оригинальном MVP терминах):

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

Конкретный ProfileRouterImpl работает с UINavigationController — UIKit-зависимость изолирована. Presenter получает Router через DI и вызывает router.navigateToEditProfile(user:) — без знания о том, что происходит под капотом.

Когда MVP лучше MVVM

MVP удобен, когда команда активно пишет unit-тесты для логики экранов и не хочет добавлять Combine как зависимость. Также — при большом количестве UIKit-экранов с UITableView / UICollectionView: Presenter удобно обрабатывает события делегатов, View просто пробрасывает вызовы.

На SwiftUI-проектах MVP неорганичен — там нет UIViewController, паттерн не ложится естественно.

Что настраиваем

Проектируем базовые протоколы View, Presenter, Router → создаём фабрику модулей (каждый экран — ProfileModule.build() возвращает UIViewController) → настраиваем DI → создаём пример модуля с тестами → при необходимости мигрируем существующие MVC-экраны.

Работа занимает 2–3 дня для нового проекта.