Ответ
В чистой реализации 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.