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

Может ли Coordinator управлять массивом координаторов?

1.0 Junior🔥 251 комментариев
#Коллекции и структуры данных

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

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

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

Может ли Coordinator управлять массивом координаторов?

Да, абсолютно. Более того, это одна из ключевых концепций, лежащих в основе эффективной и масштабируемой архитектуры Coordinator в iOS-приложениях. Управление массивом дочерних координаторов — это механизм, который позволяет выстраивать иерархию координаторов, аналогичную иерархии навигационных контроллеров или дочерних контроллеров. Это превращает простой координатор из объекта, управляющего одним потоком, в супервизора (Supervisor Coordinator) или координатора-родителя (Parent Coordinator), ответственного за целую ветку навигации приложения.

Зачем это нужно?

Использование массива координаторов решает несколько критически важных задач:

  1. Управление памятью и жизненным циклом: Координатор, добавленный в массив родительского координатора, удерживается сильной ссылкой. Когда дочерний координатор завершает свою работу (например, пользователь закрыл экран или завершил поток авторизации), родительский координатор должен удалить его из массива. Это приводит к освобождению памяти, занятой дочерним координатором и всеми его зависимостями (вью-контроллерами, сервисами). Это прямой способ борьбы с утечками памяти.

  2. Организация навигационной логики: Сложные фичи приложения (например, процесс оформления заказа из нескольких шагов или модуль с таб-баром) могут быть инкапсулированы в отдельный координатор. Родительский координатор (например, AppCoordinator) лишь запускает этот дочерний координатор, не вникая в его внутреннюю навигацию.

  3. Повторное использование координаторов: Координатор, управляющий определенным потоком (например, AuthCoordinator), может быть запущен из разных мест приложения (из AppCoordinator при старте, из MainTabCoordinator при попытке доступа к профилю). Родительский координатор просто хранит ссылку на него в своем массиве.

  4. Соблюдение принципа единственной ответственности (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) — это стандартный паттерн для построения иерар
Может ли Coordinator управлять массивом координаторов? | PrepBro