Ответ
Связь между Coordinator и ViewController строится по принципу инверсии зависимостей: ViewController сообщает о событиях, а Coordinator принимает решения о навигации. Вот основные паттерны:
1. Через замыкания (Closures) — популярный и гибкий способ Coordinator внедряет замыкания во ViewModel или напрямую во ViewController.
// В Coordinator
func showLoginScreen() -> UIViewController {
let viewModel = LoginViewModel()
// ViewModel получает замыкание для навигации
viewModel.onLoginSuccess = { [weak self] user in
self?.showHomeScreen(for: user)
}
viewModel.onSignUpTapped = { [weak self] in
self?.showSignUpScreen()
}
return LoginViewController(viewModel: viewModel)
}
2. Через протоколы/делегаты (Protocols/Delegates) ViewController объявляет делегата, который реализует Coordinator.
// Протокол для событий ViewController
protocol ProductListDelegate: AnyObject {
func didSelectProduct(_ product: Product)
func didTapFilters()
}
// ViewController хранит слабую ссылку на делегата
class ProductListViewController: UIViewController {
weak var delegate: ProductListDelegate?
private func productSelected(_ product: Product) {
delegate?.didSelectProduct(product) // Сообщаем координатору
}
}
// Coordinator реализует протокол
class AppCoordinator: ProductListDelegate {
func start() -> UIViewController {
let vc = ProductListViewController()
vc.delegate = self // Устанавливаем связь
return vc
}
// MARK: - ProductListDelegate
func didSelectProduct(_ product: Product) {
let detailVC = makeDetailVC(for: product)
navigationController.pushViewController(detailVC, animated: true)
}
}
3. Через Router (отдельный объект) Навигационная логика выносится в отдельный объект Router, которым управляет Coordinator.
Критически важные правила:
- Всегда используйте
[weak self]в замыканиях илиweak var delegate, чтобы избежать циклов сильных ссылок (retain cycles). - ViewController не должен знать о других ViewController'ах. Он только сообщает о событии («пользователь нажал кнопку»).
- Весь поток навигации (последовательность экранов) управляется Coordinator. Это делает его центральным узлом для тестирования и изменения навигации.