Какой компонент в архитектуре MVVM+Router обычно владеет роутером и управляет навигацией?

«Какой компонент в архитектуре MVVM+Router обычно владеет роутером и управляет навигацией?» — вопрос из категории Архитектура, который задают на 10% собеседований IOS Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В чистой реализации MVVM+Router (или Coordinator) ответственность за навигацию делегируется ViewModel. Это позволяет соблюдать принцип единой ответственности.

Почему ViewModel, а не ViewController?

  • ViewModel содержит бизнес-логику и знает, когда должен произойти переход (например, после успешной загрузки данных или нажатия кнопки).
  • ViewController (View) остаётся «глупым»: он только отображает состояние из ViewModel и передаёт ей пользовательские события. Он не должен принимать решения о навигации.
  • Router/Coordinator инкапсулирует как выполняется переход (например, present или push).

Пример взаимодействия:

// ViewModel решает о переходе
class LoginViewModel {
    private let router: LoginRouter

    func loginButtonTapped() {
        // Проверка данных...
        router.showHomeScreen() // Команда на навигацию
    }
}

// Router выполняет переход
class LoginRouter {
    weak var viewController: UIViewController?

    func showHomeScreen() {
        let homeVC = HomeViewController()
        viewController?.navigationController?.pushViewController(homeVC, animated: true)
    }
}

// ViewController только передаёт событие
class LoginViewController: UIViewController {
    var viewModel: LoginViewModel!

    @IBAction func loginTapped(_ sender: Any) {
        viewModel.loginButtonTapped()
    }
}

Такой подход делает код тестируемым (можно замокать Router) и отделяет логику от UIKit.