- July 26, 2025
- Mins Read
This framework uses three main concepts:
A destination could be a screen, a modal or even a dismiss action. Destinations are typically represented via enum
s.
enum AppDestination {
case first
case second
case third
}
There are three steps to defining a coordinator:
UIViewControllerCoordinator
or AppKitOrUIKitWindowCoordinator
transition(for:)
, which is responsible for mapping a route to a transition.
class AppCoordinator: AppKitOrUIKitWindowCoordinator<AppDestination> {
override func transition(for route: AppDestination) -> ViewTransition {
switch route {
case .first:
return .present(Text(“First”))
case .second:
return .push(Text(“Second”))
case .third:
return .set(Text(“third”))
}
}
}
Coordinators can be integrated in a fashion similar to @EnvironmentObject
. For this example, we’ll create an instance of the AppCoordinator
defined in the previous section, and pass it to a ContentView
via the View/coordinator(_:)
function.
ContentView
uses the coordinator via a special property wrapper, @Coordinator
, which gives access to the nearest available coordinator for a given route type at runtime (in this case, AppCoordinator
).
@main
struct App: SwiftUI.App {
@StateObject var coordinator = AppCoordinator()
var body: some Scene {
WindowGroup {
NavigationView {
ContentView()
.coordinator(coordinator)
}
}
}
}
struct ContentView: View {
@Coordinator(for: AppDestination.self) var coordinator
var body: some View {
VStack {
Button(“First”) {
coordinator.trigger(.first)
}
Button(“Second”) {
coordinator.trigger(.second)
}
Button(“Third”) {
coordinator.trigger(.third)
}
}
}
}
If you wish to provide a scoped coordinator for a child view in SwiftUI, you can use View.coordinate
to create an ad-hoc coordinator.
struct ContentView: View {
private enum MyRoute {
case foo
case bar
}
var body: some View {
NavigationView {
ChildView()
}
.coordinate(MyRoute.self) { route in
switch route {
case .foo:
return .push(Text(“Foo”))
case .bar:
return .present(Text(“Bar”))
}
}
}
private struct ChildView: View {
@Coordinator(for: MyRoute.self) var coordinator
var body: some View {
VStack {
Button(“Foo”) {
coordinator.trigger(.foo)
}
Button(“Bar”) {
coordinator.trigger(.bar)
}
}
}
}
}
In this example ContentView
creates an ad-hoc coordinator via .coordinate(MyRoute.self) { .. }
and provides it to a NavigationView
containing ChildView
.
ChildView
can now access this coordinator using the @Coordinator
property wrapper referencing the route type MyRoute
declared inside ContentView
.
In this example, only ContentView
and types defined within its namespace can access MyRoute
as it is marked as a private enum
. It is good practice to scope your routes tightly wherever possible, as it allows you to reason about your navigation flows in a simpler way.
If you need lower level access to the underlying UIViewController
or UIWindow
, use ViewTransition.custom
to implement a custom transition.
In the following example, MyRoute.foo
is implemented via a standard ViewTransition
whereas MyRoute.bar
is implemented as a custom one.
import Coordinator
import UIKit
enum MyRoute {
case foo
case bar
}
class MyViewCoordinator: UIViewControllerCoordinator<MyRoute> {
override func transition(for route: MyRoute) -> ViewTransition {
switch route {
case .foo:
return .present(Text(“Foo”))
case .bar:
return .custom {
guard let rootViewController = self.rootViewController else {
return assertionFailure()
}
// Use `rootViewController` to perform a custom transition.
rootViewController.present(
UIViewController(),
animated: true,
completion: { }
)
}
}
}
}
NavigationKit is a lightweight library which makes SwiftUI navigation super easy to use. 💻 Installation 📦 Swift Package Manager Using Swift Package Manager, add ...
An alternative SwiftUI NavigationView implementing classic stack-based navigation giving also some more control on animations and programmatic navigation. NavigationStack Installation ...
With SwiftUI Router you can power your SwiftUI app with path-based routing. By utilizing a path-based system, navigation in your app becomes ...
This package takes SwiftUI's familiar and powerful NavigationStack API and gives it superpowers, allowing you to use the same API not just ...