Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/SwiftUIX/Coordinator
A declarative navigation API for SwiftUI.
https://github.com/SwiftUIX/Coordinator
Last synced: about 2 months ago
JSON representation
A declarative navigation API for SwiftUI.
- Host: GitHub
- URL: https://github.com/SwiftUIX/Coordinator
- Owner: SwiftUIX
- License: mit
- Created: 2020-05-07T17:00:25.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2024-08-22T05:51:00.000Z (5 months ago)
- Last Synced: 2024-11-14T19:43:21.010Z (about 2 months ago)
- Language: Swift
- Size: 104 KB
- Stars: 257
- Watchers: 3
- Forks: 23
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-swiftui-libraries - Coordinator - A declarative navigation API for SwiftUI. (Navigation / Content)
README
# Concepts
This framework uses three main concepts:
- **Route** - an identifiable value that represents a destination in your app
- **Transition** - a visual transition to be applied on a view
- **Coordinator** - an object that maps **routes** to **transitions** and applies it on the current view hierarchy.# Getting Started
## Basics
### Definite a set of destinations
A destination could be a screen, a modal or even a dismiss action. Destinations are typically represented via `enum`s.
```swift
enum AppDestination {
case first
case second
case third
}
```### Define a coordinator
There are three steps to defining a coordinator:
1. You must subclass either `UIViewControllerCoordinator` or `AppKitOrUIKitWindowCoordinator`
2. You must parametrize your subclass with a **route**.
3. You must override and implement the function `transition(for:)`, which is responsible for mapping a **route** to a **transition**.```swift
class AppCoordinator: AppKitOrUIKitWindowCoordinator {
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"))
}
}
}
```### Integrate your coordinator
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`).
```swift
@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)
}
}
}
}
```## Ad-hoc Coordinators
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.
```swift
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 coordinatorvar 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.
## Custom Transitions
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.
```swift
import Coordinator
import UIKitenum MyRoute {
case foo
case bar
}class MyViewCoordinator: UIViewControllerCoordinator {
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: { }
)
}
}
}
}
```**Note:** Refrain from adding side-effects or business logic to your custom transition block. A `ViewCoordinator` is only supposed to handle transitions. Adding anything beyond transition logic breaks the conceptual model of a coordinator.