← Назад к вопросам

Как подготовишь приложение к переходу от MVC на VIPER?

2.2 Middle🔥 161 комментариев
#Soft Skills и карьера#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Подготовка приложения к переходу с MVC на VIPER

Переход от классического MVC (Model-View-Controller) к VIPER (View-Interactor-Presenter-Entity-Router) — это серьёзная архитектурная рефакторинг, требующий методичного подхода. Вот пошаговая стратегия, которую я применяю на практике.

1. Анализ и планирование

Первым делом необходимо провести аудиторскую проверку текущего кода:

  • Выделить наиболее проблемные модули с массивными ViewControllers (так называемый "Massive View Controller").
  • Определить сценарии с высокой сложностью бизнес-логики, навигации или частыми изменениями.
  • Оценить связанность модулей. VIPER идеально подходит для изолированных, функционально завершённых модулей (например, экран авторизации, детализация товара, корзина покупок).
  • Важно: Не пытаться переписать всё приложение сразу. Выберите пилотный, некритичный модуль для отработки подхода и создания шаблонов.

2. Создание инфраструктуры и шаблонов

VIPER требует большего количества файлов и чётких протоколов. Я создаю базовые структуры:

  • Протоколы (Protocols) для каждого слоя, определяющие контракты.
  • Базовые классы или сабмодули для повторяющихся задач (например, базовый Presenter с weak-ссылкой на view).
  • Сборщик модуля (ModuleBuilder/Assembler). Это ключевой класс, который инкапсулирует логику создания всего стека VIPER. Это упрощает Dependency Injection и тестирование.
// Пример протокола для View
protocol LoginViewInput: AnyObject {
    func showError(_ message: String)
    func updateUI(with state: LoginViewState)
}

// Пример протокола для Presenter
protocol LoginViewOutput {
    func viewDidLoad()
    func didTapLogin(with credentials: Credentials)
}

3. Инкрементальный рефакторинг пилотного модуля

Работа ведётся внутри выбранного модуля, без нарушения работы остального приложения.

  • Вынос бизнес-логики: Вся логика из Massive ViewController переносится в Interactor. ViewController не должен знать о моделях данных (Entity) или сетевых запросах.
    // Было в MVC:
    class OldViewController: UIViewController {
        func loginButtonTapped() {
            let email = emailTextField.text ?? ""
            // ... валидация, сетевой запрос, обработка ответа, переход
        }
    }
    
    // Стало в VIPER (в Interactor):
    class LoginInteractor {
        weak var output: LoginInteractorOutput!
        private let authService: AuthServiceProtocol
    
        func login(with credentials: Credentials) {
            authService.login(credentials) { [weak self] result in
                switch result {
                case .success(let user):
                    self?.output.loginDidSucceed(user)
                case .failure(let error):
                    self?.output.loginDidFail(error)
                }
            }
        }
    }
    
  • Вынос навигации: Вся логика переходов (performSegue, present, push) выносится в Router. Presenter только сообщает роутеру о необходимости перехода.
  • Преобразование ViewController: Он становится пассивной View, реализующей протокол. Его задачи: отображать данные от Presenter и уведомлять Presenter о действиях пользователя.

4. Настройка взаимодействия и Dependency Injection

  • Все зависимости (сервисы, роутеры) передаются через инициализаторы, а не создаются внутри классов. Это принцип Dependency Injection.
  • Для управления зависимостями часто подключаю легковесный DIP-контейнер (Swinject, PureDI) или использую кастомный Assembler.
  • Связи между слоями устанавливаются только через протоколы (абстракции), что является основой для чистой архитектуры и мокинга в тестах.

5. Внедрение тестирования

Одна из главных целей VIPER — тестируемость.

  • Unit-тесты пишутся параллельно с рефакторингом для каждого слоя.
  • Interactor тестируется легко, так как он не зависит от UIKit.
  • Presenter тестируется с моками View, Interactor и Router.
  • View (UI-логика) и Router часто покрываются Snapshot- или UI-тестами.

6. Интеграция и постепенное расширение

  • После успешного внедрения и тестирования пилотного модуля, паттерн распространяется на новые модули приложения.
  • Для старых модулей применяется стратегия постепенного вытеснения: новый функционал добавляется по VIPER, а при значительных изменениях в старом модуле — он рефакторится.
  • Координация модулей: Для управления потоком между VIPER-модулями можно ввести дополнительный слой — координатор (Coordinator/FlowController), который будет работать с Router'ами.

Ключевые риски и советы

  • Бойлерплейт-код: Количество файлов возрастает. Решение: использовать ** кодогенерацию (Xcode Templates, Sourcery)** для создания каркасов модулей.
  • Сложность для маленьких команд/проектов: VIPER — это "архитектура для взрослых". Для простых приложений он может быть избыточен. Всегда нужно оценивать соотношение затрат и выгод.
  • Обучение команды: Провожу воркшопы и код-ревью, чтобы все члены команды понимали принципы и конвенции.

Итог: Переход на VIPER — это долгосрочная инвестиция в поддерживаемость, тестируемость и распределение ответственности в коде. Главное — действовать эволюционно, постоянно валидируя подход на практике, и не бояться адаптировать чистую архитектуру под конкретные нужды проекта.

Как подготовишь приложение к переходу от MVC на VIPER? | PrepBro