В чем разница между паттернами Coordinator и Router в iOS?

Ответ

Coordinator и Router — это архитектурные паттерны для управления навигацией, но с разной областью ответственности.

Coordinator (Координатор):

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

Пример Coordinator:

class AppCoordinator {
    private let window: UIWindow
    private var childCoordinators = [Coordinator]()

    func start() {
        if UserDefaults.standard.isLoggedIn {
            showMainFlow()
        } else {
            showAuthFlow()
        }
    }

    private func showAuthFlow() {
        let authCoordinator = AuthCoordinator(window: window)
        authCoordinator.onSuccess = { [weak self] in
            self?.childCoordinators.removeAll()
            self?.showMainFlow()
        }
        childCoordinators.append(authCoordinator)
        authCoordinator.start()
    }
}

Router (Роутер):

  • Отвечает только за механику перехода между конкретными ViewController'ами (push, present, dismiss).
  • Не содержит бизнес-логики. Не знает, почему происходит переход, только как его выполнить.
  • Обычно является слабой ссылкой (weak) от View или Presenter.
  • Более простой и декларативный.

Пример Router (в VIPER):

protocol ProfileRoutingLogic {
    func routeToSettings()
}

class ProfileRouter: ProfileRoutingLogic {
    weak var viewController: UIViewController?

    func routeToSettings() {
        let settingsVC = SettingsViewController()
        // Чистая механика перехода, без логики
        viewController?.navigationController?.pushViewController(settingsVC, animated: true)
    }
}
Сравнение: Аспект Coordinator Router
Ответственность Поток приложения, бизнес-логика навигации Механика переходов между VC
Сложность Высокая, управляет иерархией Низкая, выполняет конкретные команды
Использование Часто как центральный элемент архитектуры Компонент в паттернах типа VIPER/Clean Swift
Связность Сильная с логикой приложения Слабая, зависит только от интерфейсов

На практике Coordinator может использовать Router внутри себя для выполнения конкретных переходов, разделяя логику и механику.

Ответ 18+ 🔞

Да ты посмотри, какие умные слова придумали! Coordinator и Router, блядь. Сидят два паттерна, оба навигацией занимаются, а один — как начальник цеха, а второй — как разнорабочий с тачкой. Сейчас разберём эту хуйню, чтобы мозг не взорвался.

Ну, Coordinator — это, сука, главный по потокам. Он как режиссёр в этом ёбаном театре твоего приложения. Знает всю подноготную: залогинился юзер или нет, куда его после покупки слать, какую хуйню показывать дальше. У него в голове — вся бизнес-логика навигации. Он ещё и других координаторов плодит, иерархию строит, царь и бог. С ним приложение не развалится на отдельные экраны, которые друг про друга ни хуя не знают.

Вот, смотри, как он может выглядеть в коде, не моргай:

class AppCoordinator {
    private let window: UIWindow
    private var childCoordinators = [Coordinator]()

    func start() {
        if UserDefaults.standard.isLoggedIn {
            showMainFlow()
        } else {
            showAuthFlow()
        }
    }

    private func showAuthFlow() {
        let authCoordinator = AuthCoordinator(window: window)
        authCoordinator.onSuccess = { [weak self] in
            self?.childCoordinators.removeAll()
            self?.showMainFlow()
        }
        childCoordinators.append(authCoordinator)
        authCoordinator.start()
    }
}

Видишь? Он решает, куда пользователя направить. Царь, блядь, горы. А теперь про второго.

Router — это уже не начальник, а, прости господи, инструмент. Отвёртка. Его задача — тупо выполнить команду «перейти из точки А в точку Б». Пушнуть, презентнуть, диcмиснуть. Он не думает, почему это нужно сделать. Его дело — как это сделать. Обычно он прицеплен к какому-нибудь ViewController'у слабой ссылкой, чтобы не создавать циклических зависимостей, этих ёбаных retain cycle.

Вот, например, в этой вашей архитектуре VIPER он выглядит как-то так:

protocol ProfileRoutingLogic {
    func routeToSettings()
}

class ProfileRouter: ProfileRoutingLogic {
    weak var viewController: UIViewController?

    func routeToSettings() {
        let settingsVC = SettingsViewController()
        // Чистая механика перехода, без логики
        viewController?.navigationController?.pushViewController(settingsVC, animated: true)
    }
}

Никакой логики! Получил команду — выполнил. Робот, блядь, одноразовый.

Так в чём разница, спросишь ты? Да вот, смотри табличку, я для таких, как ты, нарисовал:

Штука Coordinator Router
Чем занят Управляет всем потоком, думает головой Тупо тыкает кнопки перехода
Сложность Высокая, иерархии, логика — пиздец Низкая, как палка-копалка
Где юзают Часто как стержень всей архитектуры Как винтик в VIPER или Clean Swift
Связанность Крепко впаян в логику приложения Почти независим, знает только интерфейсы

А на практике, умные ребята часто комбинируют: Coordinator рулит логикой, а для грязной работы — тыканья вьюконтроллеры — использует внутри себя какого-нибудь простенького Router'а. Разделение труда, ёпта! Один думает, другой — руки моет. Красота, в рот меня чих-пых!