Coordinator: Schlüsselkonzepte, Koordinator-Muster für iOS-Navigation

Autor: IT Sectr Veröffentlicht: 2026-02-18 Lesezeit: 9 Min.

Coordinator (Koordinator) — ein architektonisches Navigationsmuster, das die Logik von Übergängen zwischen Bildschirmen aus dem ViewController in separate Klassen auslagert. Das Muster wurde 2015 von Soroush Khanlou vorgeschlagen und hat sich in der iOS-Community weit verbreitet. Der Coordinator verwaltet den Anwendungsfluss: er erstellt und zeigt ViewController an, übergibt Daten zwischen Bildschirmen und behandelt den Flussabschluss. Das Muster löst das Problem des Massive View Controllers, indem es die Navigation aus dem Controller auslagert. Mehr Details im Originalartikel über Coordinator.

Wichtige Punkte

  • Coordinator — Navigationsmuster, das Übergangslogik aus dem ViewController auslagert
  • Trennung der Verantwortlichkeiten — ViewController verwaltet UI, Coordinator die Navigation
  • Router — Hilfskomponente des Coordinator zur Abstraktion von UINavigationController
  • Flow — Bildschirmsequenz, die von einem Coordinator verwaltet wird (z. B. Onboarding)
  • Delegierung — untergeordnete Koordinatoren berichten dem Eltern via delegate/protocol

Was ist Coordinator: Wesen des Navigationsmusters

Coordinator — ein Muster, das die Verantwortung für die Navigation in einer iOS-Anwendung übernimmt. Im Standard-UIKit verwaltet der ViewController selbst die Übergänge: present, push, show segue — alle Navigationsmethoden werden von UIViewController aufgerufen. Der Coordinator lagert diese Logik aus: der ViewController meldet ein Ereignis (z. B. «der Benutzer hat den Login-Button geklickt»), der Coordinator entscheidet, welcher Bildschirm als nächstes angezeigt wird. Der ViewController behält nur die UI-Logik und delegiert die Navigation an den Koordinator.

Musterstruktur — CoordinatorProtocol mit den Methoden start() und finish(). start() — Beginn eines Flusses: Erstellung des ersten ViewControllers und Anzeige. finish() — Abschluss des Flusses mit Benachrichtigung des Elternkoordinators. Router — ein Wrapper um UINavigationController (oder UISplitViewController), der Methoden show, push, pop, dismiss bereitstellt. Der Koordinator arbeitet nicht direkt mit UINavigationController — nur über Router. Dies ermöglicht das Testen der Navigation und den Wechsel des UI-Frameworks.

KomponenteRolleBeispiel
CoordinatorVerwaltung des NavigationsflussesAuthCoordinator, ProfileCoordinator
RouterAbstraktion über UINavigationControllerpush, present, pop, dismiss
ViewControllerUI + Ereignisdelegierung an CoordinatorLoginViewController.delegate

Probleme, die Coordinator löst — Massive View Controller (Navigation ist eine häufige Ursache für Controller-Aufblähung). Im Standard-UIKit enthält ViewController prepareForSegue, Navigationsdelegaten, Behandlung von unwind segues. Coordinator beseitigt dies. Segues im Storyboard sind statische Verbindungen zwischen Bildschirmen, Coordinator bietet dynamische Navigation mit Bedingungen. Das Testen der Navigation wird möglich: Coordinator kann ohne UI getestet werden, indem die Sequenz der Router-Aufrufe überprüft wird.

Coordinator in Swift: Implementierung mit Router und Flow

Basis-Coordinator in Swift — ein Protokoll mit einem assoziierten Typ für Router und den Methoden start/finish. Router — ein Protokoll, das UINavigationController abstrahiert. Eine konkrete Router-Implementierung umschließt UINavigationController und delegiert Methoden an ihn. Der Coordinator akzeptiert Router im init und verwendet ihn für die Navigation. Untergeordnete Koordinatoren werden im Array childCoordinators zur Lebenszyklusverwaltung gespeichert.

swift
// Router — Navigationsabstraktion
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 — Flussverwaltung
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)
    }
}

Erstellung des Coordinator in AppDelegate/SceneDelegate — AppDelegate oder SceneDelegate erstellt UINavigationController, umschließt ihn in NavigationRouter, erstellt einen root-Coordinator (AppCoordinator) und ruft start() auf. AppCoordinator entscheidet, ob Onboarding, Login oder der Hauptbildschirm angezeigt wird — abhängig vom Anwendungszustand. Coordinator ist der einzige Einstiegspunkt für die Navigation, ViewController kennt keine anderen Bildschirme.

Untergeordnete Coordinator und Koordinatorhierarchie

Koordinatorhierarchie — AppCoordinator → AuthCoordinator/MainCoordinator → ProfileCoordinator/SettingsCoordinator. Der untergeordnete Koordinator wird vom Eltern erstellt und im childCoordinators-Array gespeichert. Wenn der untergeordnete Koordinator seine Arbeit abschließt, ruft er finish() beim Eltern auf, und der Elternteil entfernt ihn aus childCoordinators. Dies verhindert Speicherlecks: Coordinator hat eine starke Referenz auf ViewController (über Router), und ohne Entfernung aus childCoordinators wird das Objekt nicht freigegeben.

swift
// Delegate für Coordinator -> Parent-Kommunikation
protocol AuthCoordinatorDelegate: AnyObject {
    func authCoordinatorFinished(_ coordinator: AuthCoordinator)
}

class AuthCoordinator: CoordinatorProtocol {
    weak var delegate: AuthCoordinatorDelegate?

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

// AppCoordinator — Eltern
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()
    }
}

Verwaltung von childCoordinators — das Entfernen eines Coordinator aus dem Array ist der einzige Weg, ihn freizugeben. Wenn Sie vergessen, einen abgeschlossenen Coordinator zu entfernen, bleibt er zusammen mit seinen ViewControllern im Speicher. Empfohlene Ansätze: didMove(toParent:) des Elternteils, Callback bei Abschluss oder Combine-Publisher zur automatischen Entfernung. Das Coordinator-Muster spezifiziert keinen Benachrichtigungsmechanismus — delegate, closure oder Combine — die Wahl liegt beim Entwickler.

Möglichkeiten der Datenübergabe zwischen Koordinatoren

Datenübergabe via delegate — der untergeordnete Coordinator definiert ein delegate-Protokoll mit Methoden, über die Ergebnisse übergeben werden: func authCoordinator(_:didLoginWith user: User). Der Elternteil implementiert das Protokoll und erhält die Daten beim Abschluss des untergeordneten Flusses. Dies ist typsicher und explizit. Nachteil: jeder untergeordnete Coordinator benötigt ein separates Protokoll. Bei Projekten mit 10+ Koordinatoren führt dies zu einer Zunahme der Dateien.

Datenübergabe via Result-Typ — die finish-Methode akzeptiert Result, wobei Output ein generischer Typ des Flussergebnisses ist. Coordinator — ein Generikum mit assoziiertem Ergebnistyp. start() mit Callback: start(completion: @escaping (Output) -> Void). Dies reduziert den Code: kein separates Protokoll für jeden Koordinator nötig. RxSwift/Combine: Coordinator veröffentlicht das Ergebnis über PassthroughSubject/Publisher. Die Wahl hängt vom architektonischen Ansatz des Teams ab.

MethodeVorteileNachteile
DelegateTypsicher, explizit, getrennte ProtokolleViele Protokolle, viel Boilerplate
ClosureKompakt, weniger DateienSchwer zu debuggende retain cycles
Combine/RxReaktiv, leicht kombinierbarBibliotheksabhängigkeit, schwieriger zu debuggen

Gemeinsame Datenebene — Koordinatoren übergeben Daten nicht direkt, sondern verwenden einen gemeinsamen Dienst/ein gemeinsames Repository. AuthCoordinator speichert das Token in Keychain/UserDefaults, ProfileCoordinator liest von dort. Koordinatoren kommunizieren über gemeinsamen Zustand (Dependency Injection Container) statt direkter Aufrufe. Dies reduziert die Kopplung zwischen Koordinatoren, erzeugt aber implizite Abhängigkeiten vom gemeinsamen Zustand.

Vergleich von Coordinator mit Router, VIPER und MVVM-C

Coordinator vs Router — Router ist eine Komponente des Coordinator, die UINavigationController abstrahiert. Coordinator ist für den Fluss verantwortlich (welcher Bildschirm angezeigt wird), Router — für die Mechanik (wie anzeigen: push/present). Router ist das «Wie», Coordinator das «Was». Router kann ohne Coordinator verwendet werden (z. B. Navigator-Singleton), aber Coordinator ohne Router ist nur ein ViewController mit einer anderen Abstraktion. Üblicherweise werden beide Muster zusammen verwendet.

Coordinator vs VIPER — VIPER hat eine Wireframe-Komponente, die für die Navigation verantwortlich ist — analog zum Coordinator. In VIPER ist Wireframe Teil des Moduls, Coordinator ist eine separate Schicht über den Modulen. Ein VIPER-Modul (View-Interactor-Presenter-Entity-Router) enthält Navigation als Teil des Moduls. Coordinator ist extern zu den Modulen: er erstellt und verbindet Module, ist aber nicht Teil von ihnen. Coordinator ist flexibler für die Wiederverwendung von Bildschirmen in verschiedenen Flüssen.

MVVM-C — eine Erweiterung von MVVM mit Coordinator. ViewModel kennt den Coordinator nicht direkt — ViewController delegiert die Navigation über ViewModel, ViewModel ruft den coordinator über ein Protokoll auf. MVVM-C ist der Standardansatz für iOS-Projekte mit SwiftUI: Coordinator verwaltet NavigationStack oder fullScreenCover, ViewModel ruft den coordinator durch Veröffentlichung des Zustands auf. Apple empfiehlt Coordinator nicht für SwiftUI — NavigationStack und NavigationPath sind integrierte Navigationsmechanismen.

swift
// MVVM-C: ViewModel ruft Coordinator über ein Protokoll auf
protocol AuthNavigationProtocol: AnyObject {
    func showMainScreen()
    func showForgotPassword()
}

class AuthViewModel: ObservableObject {
    weak var navigation: AuthNavigationProtocol?

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

Häufig gestellte Fragen

Braucht man Coordinator für SwiftUI?

Für SwiftUI ersetzt die integrierte Navigation (NavigationStack, NavigationPath) oft den Coordinator. Apple empfiehlt path-basierte Navigation. Coordinator ist sinnvoll für komplexe Flüsse mit tiefen Bedingungen (Onboarding-Login-Hauptbildschirm je nach Rolle). Für einfache SwiftUI-Anwendungen ist Coordinator überflüssig — verwenden Sie NavigationPath.

Ist Coordinator ein Router?

Nein, das sind unterschiedliche Muster. Coordinator verwaltet den Navigationsfluss: entscheidet, welcher Bildschirm angezeigt wird, erstellt ViewControllers und verbindet sie. Router ist eine Abstraktion über UINavigationController: push, present, pop, dismiss. Coordinator verwendet Router zur Navigationsausführung. In einigen Implementierungen enthält Router Coordinator-Logik (Router-per-screen), aber dies weicht vom ursprünglichen Muster ab.

Wie vermeidet man retain cycles im Coordinator?

Zwei Hauptquellen von Lecks: childCoordinators (Eltern behalten Kind, vergessen zu entfernen) und Router (UINavigationController behält ViewController). Lösung: untergeordneten Coordinator bei finish() immer aus dem Array entfernen. Für delegate weak-Referenz verwenden. Für Router — keine starke Referenz auf UINavigationController halten, wenn er bereits in der window-Hierarchie ist. Testen Sie den deinit des Coordinator.

Wann ist Coordinator überflüssig?

Für Anwendungen mit 3-5 Bildschirmen ist Coordinator überflüssig — segue oder einfaches navigationController.pushViewController ist einfacher. Für SwiftUI-Anwendungen mit NavigationStack ebenfalls überflüssig. Coordinator ist gerechtfertigt für Anwendungen mit 15+ Bildschirmen, komplexen Flüssen (Onboarding mit Verzweigungen, Autorisierung mit Passwortwiederherstellung) und gemischten UIKit/SwiftUI-Projekten.

Wie testet man Coordinator?

Mock Router — überprüfen, welche Methoden mit welchen Parametern aufgerufen werden. childCoordinators prüfen: nach start() ist das Array nicht leer, nach finish() — leer. Coordinator wird ohne UI getestet: Router ist ein Protokoll, sein Mock benötigt kein UIKit. Verwenden Sie XCTestExpectation für asynchrone Flüsse. In Android — ähnliches Testen von NavigationController und NavHost mit Mock-Navigation.

Zusammenfassung

  • Coordinator — Navigationsmuster, das Übergänge aus ViewController in eine separate Klasse auslagert
  • Router — vom Coordinator verwendete Abstraktion von UINavigationController
  • Hierarchie — Eltern- und Kind-Coordinatoren mit Ergebnisdelegierung
  • MVVM-C — Standardansatz für UIKit-Projekte mit Coordinator
  • SwiftUI — integrierte Navigation via NavigationStack ersetzt Coordinator
  • Testen — Coordinator wird durch mock Router ohne UIKit getestet

Wir entwickeln eine mobile Applikation schlüsselfertig

IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.

Projekt besprechen

Lesen Sie auch