Может ли Coordinator управлять массивом координаторов?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Может ли Coordinator управлять массивом координаторов?
Да, абсолютно. Более того, это одна из ключевых концепций, лежащих в основе эффективной и масштабируемой архитектуры Coordinator в iOS-приложениях. Управление массивом дочерних координаторов — это механизм, который позволяет выстраивать иерархию координаторов, аналогичную иерархии навигационных контроллеров или дочерних контроллеров. Это превращает простой координатор из объекта, управляющего одним потоком, в супервизора (Supervisor Coordinator) или координатора-родителя (Parent Coordinator), ответственного за целую ветку навигации приложения.
Зачем это нужно?
Использование массива координаторов решает несколько критически важных задач:
-
Управление памятью и жизненным циклом: Координатор, добавленный в массив родительского координатора, удерживается сильной ссылкой. Когда дочерний координатор завершает свою работу (например, пользователь закрыл экран или завершил поток авторизации), родительский координатор должен удалить его из массива. Это приводит к освобождению памяти, занятой дочерним координатором и всеми его зависимостями (вью-контроллерами, сервисами). Это прямой способ борьбы с утечками памяти.
-
Организация навигационной логики: Сложные фичи приложения (например, процесс оформления заказа из нескольких шагов или модуль с таб-баром) могут быть инкапсулированы в отдельный координатор. Родительский координатор (например,
AppCoordinator) лишь запускает этот дочерний координатор, не вникая в его внутреннюю навигацию. -
Повторное использование координаторов: Координатор, управляющий определенным потоком (например,
AuthCoordinator), может быть запущен из разных мест приложения (изAppCoordinatorпри старте, изMainTabCoordinatorпри попытке доступа к профилю). Родительский координатор просто хранит ссылку на него в своем массиве. -
Соблюдение принципа единственной ответственности (Single Responsibility Principle): Каждый координатор отвечает только за свою область. Родительский координатор не должен знать, как создать конкретный вью-контроллер для пятого шага в дочернем потоке.
Практическая реализация
Давайте рассмотрим базовый пример. Сначала определим базовый протокол для координатора, который требует возможность управления детьми.
protocol Coordinator: AnyObject {
var childCoordinators: [Coordinator] { get set }
var navigationController: UINavigationController { get set }
func start()
}
Расширение может предоставить дефолтную реализацию для добавления и удаления дочерних координаторов:
extension Coordinator {
func addChild(_ coordinator: Coordinator) {
childCoordinators.append(coordinator)
}
func removeChild(_ coordinator: Coordinator) {
childCoordinators = childCoordinators.filter { $0 !== coordinator }
}
}
Теперь реализуем конкретного координатора (например, AppCoordinator), который будет управлять массивом дочерних координаторов.
final class AppCoordinator: Coordinator {
var childCoordinators: [Coordinator] = []
var navigationController: UINavigationController
private let window: UIWindow
init(window: UIWindow) {
self.window = window
self.navigationController = UINavigationController()
}
func start() {
window.rootViewController = navigationController
window.makeKeyAndVisible()
// Принятие решения о том, какой поток запустить
if UserDefaults.standard.isLoggedIn {
showMainFlow()
} else {
showAuthFlow()
}
}
private func showAuthFlow() {
// Создаем ДОЧЕРНИЙ координатор для потока авторизации
let authCoordinator = AuthCoordinator(navigationController: navigationController)
authCoordinator.delegate = self // Для обратной связи
addChild(authCoordinator) // Сохраняем в массиве
authCoordinator.start()
}
private func showMainFlow() {
let mainTabCoordinator = MainTabCoordinator(navigationController: navigationController)
addChild(mainTabCoordinator)
mainTabCoordinator.start()
}
}
// Протокол для обратной связи от дочернего координатора
extension AppCoordinator: AuthCoordinatorDelegate {
func authCoordinatorDidFinish(_ coordinator: AuthCoordinator) {
// Поток авторизации завершен (успешно или нет)
removeChild(coordinator) // КРИТИЧНО: удаляем из массива!
// Запускаем следующий поток
showMainFlow()
}
}
А вот как может выглядеть AuthCoordinator, который, в свою очередь, также может порождать своих дочерних координаторов (например, для экрана восстановления пароля):
final class AuthCoordinator: Coordinator {
var childCoordinators: [Coordinator] = []
var navigationController: UINavigationController
weak var delegate: AuthCoordinatorDelegate?
// ... инициализация
func start() {
let loginVC = LoginViewController()
loginVC.delegate = self
navigationController.pushViewController(loginVC, animated: true)
}
private func showForgotPasswordFlow() {
let forgotPasswordCoordinator = ForgotPasswordCoordinator(navigationController: navigationController)
forgotPasswordCoordinator.delegate = self
addChild(forgotPasswordCoordinator) // Добавляем как своего ребенка
forgotPasswordCoordinator.start()
}
}
extension AuthCoordinator: LoginViewControllerDelegate {
func loginDidSucceed() {
delegate?.authCoordinatorDidFinish(self)
}
func forgotPasswordTapped() {
showForgotPasswordFlow()
}
}
Ключевые выводы
- Массив дочерних координаторов (
childCoordinators) — это стандартный паттерн для построения иерар