Coordinator: mga pangunahing konsepto, pattern ng koordineytor para sa nabigasyon ng iOS

May-akda: IT Sectr Nai-publish: 2026-02-18 Oras ng pagbabasa: 9 min

Coordinator (koordineytor) — isang arkitektural na pattern ng nabigasyon na naglilipat ng lohika ng mga transisyon sa pagitan ng mga screen mula sa ViewController patungo sa magkakahiwalay na mga klase. Ang pattern ay iminungkahi ni Soroush Khanlou noong 2015 at naging malawak na ginagamit sa komunidad ng iOS. Coordinator ang namamahala sa flow ng aplikasyon: lumilikha at nagpapakita ng ViewController, naglilipat ng data sa pagitan ng mga screen at nagpoproseso ng pagkumpleto ng flow. Nalulutas ng pattern ang problema ng Massive View Controller sa pamamagitan ng paglalabas ng nabigasyon mula sa controller. Higit pa — sa orihinal na artikulo ng Coordinator.

Mga pangunahing punto

  • Coordinator — pattern ng nabigasyon na naglalabas ng lohika ng transisyon mula sa ViewController
  • Paghihiwalay ng responsibilidad — ViewController ang namamahala ng UI, Coordinator — ng nabigasyon
  • Router — pantulong na bahagi ng Coordinator para sa abstraksyon ng UINavigationController
  • Flow — pagkakasunod-sunod ng mga screen na pinamamahalaan ng isang Coordinator (hal., onboarding)
  • Delegasyon — mga anak na koordineytor ay nag-uulat sa magulang sa pamamagitan ng delegate/protocol

Ano ang Coordinator: esensya ng pattern ng nabigasyon

Coordinator — isang pattern na pumapalit ng responsibilidad para sa nabigasyon sa isang iOS na aplikasyon. Sa karaniwang UIKit, ang ViewController mismo ang namamahala ng mga transisyon: present, push, show segue — lahat ng paraan ng nabigasyon ay tinatawag mula sa UIViewController. Coordinator ang naglilipat ng lohika na ito: ang ViewController ay nag-uulat ng isang pangyayari (hal., „pinindot ng gumagamit ang button ng pag-login“), Coordinator ang nagpapasya kung aling screen ang susunod na ipapakita. Ang Viewcontroller ay nananatili lamang sa UI lohika at itinatagubilin ang nabigasyon sa koordineytor.

Istruktura ng pattern — CoordinatorProtocol na may mga paraang start() at finish(). start() — simula ng flow: paglikha ng unang ViewController at pagpapakita. finish() — pagkumpleto ng flow na may abiso sa magulang na koordineytor. Router — isang pambalot sa UINavigationController (o UISplitViewController) na nagbibigay ng mga paraang show, push, pop, dismiss. Ang koordineytor ay hindi direktang gumagawa sa UINavigationController — sa pamamagitan lamang ng Router. Ito ay nagpapahintulot sa pag-test ng nabigasyon at pagpapalit ng UI framework.

BahagiPapelHalimbawa
CoordinatorPamamahala ng flow ng nabigasyonAuthCoordinator, ProfileCoordinator
RouterAbstraksyon sa UINavigationControllerpush, present, pop, dismiss
ViewControllerUI + delegasyon ng mga pangyayari sa CoordinatorLoginViewController.delegate

Mga problemang nalulutas ng Coordinator — Massive View Controller (nabigasyon — karaniwang dahilan ng paglaki ng controller). Sa karaniwang UIKit, ang ViewController ay naglalaman ng prepareForSegue, mga delegado ng nabigasyon, pagproseso ng unwind segues. Inaalis ito ng Coordinator. Ang Segue sa storyboard — isang static na ugnayan sa pagitan ng mga screen, ang Coordinator ay nagbibigay ng dinamikong nabigasyon na may mga kondisyon. Ang pag-test ng nabigasyon ay nagiging posible: maaaring subukan ang Coordinator nang walang UI sa pamamagitan ng pagsusuri ng pagkakasunod-sunod ng mga tawag sa Router.

Coordinator sa Swift: implementasyon gamit ang Router at Flow

Batayang Coordinator sa Swift — isang protocol na may kaugnay na uri para sa Router at mga paraang start/finish. Router — isang protocol na nag-aabstrak ng UINavigationController. Ang konkretong implementasyon ng Router ay bumabalot sa UINavigationController at itinatagubilin dito ang mga paraan. Coordinator ay tumatanggap ng Router sa init at ginagamit ito para sa nabigasyon. Ang mga anak na koordineytor ay iniimbak sa array na childCoordinators para sa pamamahala ng siklo ng buhay.

swift
// Router — abstraksyon ng nabigasyon
protocol RouterProtocol: AnyObject {
    func push(_ viewController: UIViewController, animated: Bool)
    func pop(animated: Bool)
    func present(_ viewController: UIViewController, animated: Bool)
    func dismiss(animated: Bool)
}

final class NavigationRouter: RouterProtocol {
    private let navigationController: UINavigationController

    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }

    func push(_ vc: UIViewController, animated: Bool) {
        navigationController.pushViewController(vc, animated: animated)
    }

    func pop(animated: Bool) {
        navigationController.popViewController(animated: animated)
    }

    func present(_ vc: UIViewController, animated: Bool) {
        navigationController.present(vc, animated: animated)
    }

    func dismiss(animated: Bool) {
        navigationController.dismiss(animated: animated)
    }
}

// Coordinator — pamamahala ng flow
protocol CoordinatorProtocol: AnyObject {
    var childCoordinators: [CoordinatorProtocol] { get set }
    var router: RouterProtocol { get }
    func start()
    func finish()
}

class AuthCoordinator: CoordinatorProtocol {
    var childCoordinators: [CoordinatorProtocol] = []
    let router: RouterProtocol

    init(router: RouterProtocol) {
        self.router = router
    }

    func start() {
        let loginVC = LoginViewController()
        loginVC.onLogin = { [weak self] in
            self?.showHome()
        }
        router.push(loginVC, animated: true)
    }

    private func showHome() {
        let homeCoordinator = HomeCoordinator(router: router)
        childCoordinators.append(homeCoordinator)
        homeCoordinator.start()
    }

    func finish() {
        childCoordinators.removeAll()
        router.pop(animated: true)
    }
}

Paglikha ng Coordinator sa AppDelegate/SceneDelegate — AppDelegate o SceneDelegate ay lumilikha ng UINavigationController, binabalot ito sa NavigationRouter, lumilikha ng ugat na Coordinator (AppCoordinator) at tumatawag ng start(). AppCoordinator ang nagpapasya kung ipapakita ang onboarding, pag-login o pangunahing screen — depende sa estado ng aplikasyon. Coordinator — ang tanging punto ng pagpasok para sa nabigasyon, ang ViewController ay hindi alam ang tungkol sa iba pang mga screen.

Anak na Coordinator at hirarkiya ng mga koordineytor

Hirarkiya ng mga koordineytor — AppCoordinator → AuthCoordinator/MainCoordinator → ProfileCoordinator/SettingsCoordinator. Ang anak na koordineytor ay nilikha ng magulang at iniimbak sa array na childCoordinators. Kapag natapos ng anak na koordineytor ang kanyang gawain, tinatawag niya ang finish() sa magulang, at tinatanggal siya ng magulang mula sa childCoordinators. Ito ay pumipigil sa mga tagas ng memorya: ang Coordinator ay may malakas na sanggunian sa ViewController (sa pamamagitan ng Router), at kung hindi tatanggalin mula sa childCoordinators, ang bagay ay hindi mapapalaya.

swift
// Delegado para sa komunikasyon ng Coordinator -> Parent
protocol AuthCoordinatorDelegate: AnyObject {
    func authCoordinatorFinished(_ coordinator: AuthCoordinator)
}

class AuthCoordinator: CoordinatorProtocol {
    weak var delegate: AuthCoordinatorDelegate?

    func finish() {
        delegate?.authCoordinatorFinished(self)
    }
}

// AppCoordinator — magulang
class AppCoordinator: AuthCoordinatorDelegate {
    func startAuthFlow() {
        let authCoordinator = AuthCoordinator(router: router)
        authCoordinator.delegate = self
        childCoordinators.append(authCoordinator)
        authCoordinator.start()
    }

    func authCoordinatorFinished(_ coordinator: AuthCoordinator) {
        childCoordinators.removeAll { $0 is AuthCoordinator }
        startMainFlow()
    }
}

Pamamahala ng childCoordinators — pagtanggal ng Coordinator mula sa array ay ang tanging paraan upang mapalaya ito. Kung makalimutan mong tanggalin ang natapos na Coordinator, nananatili ito sa memorya kasama ng mga ViewController. Inirerekomenda: didMove(toParent:) ng magulang, callback sa pagkumpleto, o Combine publisher para sa awtomatikong pagtanggal. Ang pattern ng Coordinator ay hindi tumutukoy ng mekanismo ng abiso — delegate, closure o Combine — ang pagpili ay nasa developer.

Mga paraan ng paglilipat ng data sa pagitan ng mga koordineytor

Paglilipat ng data sa pamamagitan ng delegado — ang anak na Coordinator ay tumutukoy ng isang protocol ng delegado na may mga paraan kung saan inililipat ang mga resulta: func authCoordinator(_:didLoginWith user: User). Ang magulang ay nag-iimplementa ng protocol at tumatanggap ng data sa pagkumpleto ng flow ng anak. Ito ay ligtas sa uri at malinaw. Kahinaan: para sa bawat anak na Coordinator kailangang sumulat ng hiwalay na protocol. Para sa mga proyekto na may 10+ Coordinator, ito ay humahantong sa pagtaas ng bilang ng mga file.

Paglilipat ng data sa pamamagitan ng Result na uri — ang paraang finish ay tumatanggap ng Result, kung saan ang Output ay ang generic na uri ng resulta ng flow. Coordinator — isang generic na may kaugnay na uri ng resulta. start() na may callback: start(completion: @escaping (Output) -> Void). Ito ay nagpapaikli ng code: hindi kailangang sumulat ng hiwalay na protocol para sa bawat koordineytor. RxSwift/Combine: ang Coordinator ay nag-publish ng resulta sa pamamagitan ng PassthroughSubject/Publisher. Ang pagpili ay depende sa arkitektural na approach ng team.

ParaanMga kalamanganMga kahinaan
DelegateLigtas sa uri, malinaw, hiwalay na mga protocolMaraming protocol, maraming boilerplate
ClosureKompak, mas kaunting fileMahirap i-debug ang retain cycle
Combine/RxReaktibo, madaling pagsamahinDependensya sa library, mas mahirap ang Debug

Pinagsasaluhang layer ng data — Ang mga Coordinator ay hindi direktang naglilipat ng data, ngunit gumagamit ng pinagsasaluhang serbisyo/repositoryo. Ang AuthCoordinator ay nag-iimbak ng token sa Keychain/UserDefaults, ang ProfileCoordinator ay nagbabasa mula doon. Ang mga Coordinator ay nakikipag-ugnayan sa pamamagitan ng pinagsasaluhang estado (Dependency Injection container), hindi sa pamamagitan ng direktang tawag. Ito ay nagbabawas ng pagkakadugtong ng mga Coordinator, ngunit lumilikha ng mga implicit na dependensya sa pinagsasaluhang estado.

Paghahambing ng Coordinator sa Router, VIPER at MVVM-C

Coordinator vs Router — Router ay isang bahagi ng Coordinator na nag-aabstrak ng UINavigationController. Coordinator ang responsable para sa flow (aling screen ang ipapakita), Router — para sa mekanika (paano ipapakita: push/present). Router ay „paano“, Coordinator ay „ano“. Maaaring gamitin ang Router nang walang Coordinator (hal., Navigator-singleton), ngunit ang Coordinator na walang Router — isa lamang ViewController na may ibang abstraksyon. Karaniwang ginagamit ang parehong pattern nang magkasama.

Coordinator vs VIPER — Ang VIPER ay may bahaging Wireframe na responsable para sa nabigasyon — kahalintulad ng Coordinator. Sa VIPER, ang Wireframe ay bahagi ng modyul, Coordinator — isang hiwalay na layer sa itaas ng mga modyul. Ang modyul ng VIPER (View-Interactor-Presenter-Entity-Router) ay may kasamang nabigasyon bilang bahagi ng modyul. Ang Coordinator ay panlabas sa mga modyul: lumilikha at nag-uugnay ng mga modyul, ngunit hindi kabilang sa kanilang komposisyon. Ang Coordinator ay mas nababaluktot para sa muling paggamit ng mga screen sa iba't ibang flow.

MVVM-C — pagpapalawak ng MVVM gamit ang Coordinator. Ang ViewModel ay hindi direktang alam ang Coordinator — ang ViewController ay nagtatalaga ng nabigasyon sa pamamagitan ng ViewModel, ang ViewModel ay tumatawag sa koordineytor sa pamamagitan ng isang protocol. MVVM-C — ang karaniwang approach para sa mga proyekto ng iOS na may SwiftUI: Coordinator ang namamahala ng NavigationStack o fullScreenCover, ang ViewModel ay tumatawag sa koordineytor sa pamamagitan ng pag-publish ng estado. Hindi inirerekomenda ng Apple ang Coordinator para sa SwiftUI — NavigationStack at NavigationPath ay mga built-in na mekanismo ng nabigasyon.

swift
// MVVM-C: ViewModel ay tumatawag sa Coordinator sa pamamagitan ng protocol
protocol AuthNavigationProtocol: AnyObject {
    func showMainScreen()
    func showForgotPassword()
}

class AuthViewModel: ObservableObject {
    weak var navigation: AuthNavigationProtocol?

    func loginTapped() {
        // lohika...
        navigation?.showMainScreen()
    }
}

Mga madalas itanong

Kailangan ba ang Coordinator para sa SwiftUI?

Para sa SwiftUI, ang built-in na nabigasyon (NavigationStack, NavigationPath) ay kadalasang pumapalit sa Coordinator. Inirerekomenda ng Apple ang path-based na nabigasyon. Ang Coordinator ay may saysay para sa masalimuot na flow na may malalalim na kondisyon (onboarding-login-pangunahing screen depende sa papel). Para sa simpleng mga aplikasyon sa SwiftUI, ang Coordinator ay labis — gamitin ang NavigationPath.

Ang Coordinator ba ay Router?

Hindi, ito ay magkaibang mga pattern. Coordinator ang namamahala ng flow ng nabigasyon: nagpapasya kung aling screen ang ipapakita, lumilikha ng ViewController at nag-uugnay ng mga ito. Router — abstraksyon sa UINavigationController: push, present, pop, dismiss. Ginagamit ng Coordinator ang Router para isagawa ang nabigasyon. Sa ilang implementasyon, ang Router ay may kasamang lohika ng Coordinator (Router-per-screen), ngunit ito ay paglihis mula sa orihinal na pattern.

Paano maiiwasan ang retain cycle sa Coordinator?

Dalawang pangunahing lugar ng tagas: childCoordinators (hawak ng magulang ang anak, nakakalimutang tanggalin) at Router (hawak ng UINavigationController ang ViewController). Solusyon: laging tanggalin ang anak na Coordinator mula sa array sa finish(). Gumamit ng weak na sanggunian para sa delegate. Para sa Router — huwag magtago ng malakas na sanggunian sa UINavigationController kung nasa hirarkiya na ito ng window. Subukan ang deinit ng Coordinator.

Kailan labis ang Coordinator?

Para sa mga aplikasyon na may 3-5 screen, labis ang Coordinator — ang segue o simpleng navigationController.pushViewController ay mas simple. Para sa mga aplikasyon ng SwiftUI na may NavigationStack — labis din. Ang Coordinator ay makatwiran para sa mga aplikasyon na may 15+ screen, masalimuot na flow (onboarding na may mga sanga, awtorisasyon na may pagbawi ng password) at pinaghalong proyekto ng UIKit/SwiftUI.

Paano subukan ang Coordinator?

Mock Router — pagsusuri kung aling mga paraan ang tinatawag at sa anong mga parameter. Pagsusuri ng childCoordinators: pagkatapos ng start() ang array ay hindi walang laman, pagkatapos ng finish() — walang laman. Ang Coordinator ay sinusubok nang walang UI: ang Router ay isang protocol, ang mock nito ay hindi nangangailangan ng UIKit. Gamitin ang XCTestExpectation para sa async flow. Sa Android analog — pagsubok ng NavigationController at NavHost na may mock-nabigasyon.

Buod

  • Coordinator — pattern ng nabigasyon na naglilipat ng mga transisyon mula sa ViewController patungo sa hiwalay na klase
  • Router — abstraksyon ng UINavigationController na ginagamit ng Coordinator
  • Hirarkiya — magulang at anak na Coordinator na may delegasyon ng mga resulta
  • MVVM-C — karaniwang approach para sa mga proyekto ng UIKit na may Coordinator
  • SwiftUI — built-in na nabigasyon sa pamamagitan ng NavigationStack ay pumapalit sa Coordinator
  • Pagsubok — Coordinator ay sinusubok sa pamamagitan ng mock Router nang walang UIKit

Gagawa kami ng mobile application na turnkey

Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.

Pag-usapan ang proyekto

Basahin din