Routing
is a lightweight SwiftUI navigation library.
- Leverages 1st-party APIs
NavigationStack
&NavigationDestination
. - Never be confused about
NavigationLink
orNavigationPath
again! (You don't need them) - Unit Tested protocol implementations.
- Zero 3rd party dependencies.
Note - This library is for NavigationStack only.
If you need to abstract sheets, alerts, etc. then use my other library Presenting
Platform | Minimum Version |
---|---|
iOS | 16.0 |
macOS | 13.0 |
tvOS | 16.0 |
watchOS | 9.0 |
You can install Routing
using the Swift Package Manager.
- In Xcode, select
File
>Add Package Dependencies
.
- Copy & paste the following into the
Search or Enter Package URL
search bar.
https://github.com/JamesSedlacek/Routing.git
- Xcode will fetch the repository & the
Routing
library will be added to your project.
- Create a
Route
enum that conforms to theRoutable
protocol.
import Routing
import SwiftUI
enum ExampleRoute: Routable {
case detail
case settings
var body: some View {
switch self {
case .detail:
DetailView()
case .settings:
SettingsView()
}
}
}
- Create a
Router
object and wrap yourRootView
with aRoutingView
.
import SwiftUI
import Routing
struct ContentView: View {
@StateObject private var router: Router<ExampleRoute> = .init()
var body: some View {
RoutingView(router) {
Button("Go to Settings") {
router.navigate(to: .settings)
}
}
}
}
- Handle navigation using the
Router
functions
/// Navigate back in the stack by a specified count.
func navigateBack(_ count: Int)
/// Navigate back to a specific destination in the stack.
func navigateBack(to destination: Destination)
/// Navigate to the root of the stack by emptying it.
func navigateToRoot()
/// Navigate to a specific destination by appending it to the stack.
func navigate(to destination: Destination)
/// Navigate to multiple destinations by appending them to the stack.
func navigate(to destinations: [Destination])
/// Replace the current stack with new destinations.
func replace(with destinations: [Destination])
- Child Views have access to the
Router
object through the environment.
import SwiftUI
import Routing
struct SettingsView: View {
@EnvironmentObject
private var router: Router<ExampleRoute>
var body: some View {
Button("Go Back") {
router.navigateBack()
}
}
}
import Routing
import SwiftUI
enum ContentRoute: Routable {
case detail(Color)
case settings
var body: some View {
switch self {
case .detail(let color):
ColorDetail(color: color)
case .settings:
SettingsView()
}
}
}
struct ContentView: View {
@StateObject private var router: Router<ContentRoute> = .init()
private let colors: [Color] = [.red, .green, .blue]
var body: some View {
RoutingView(router) {
List(colors, id: \.self) { color in
color
.onTapGesture {
router.navigate(to: .detail(color))
}
}
}
}
}
struct ColorDetail: View {
@EnvironmentObject
private var router: Router<ContentRoute>
private let color: Color
init(color: Color) {
self.color = color
}
var body: some View {
color
.frame(maxWidth: .infinity, maxHeight: .infinity)
.toolbar { toolbarContentView }
}
private var toolbarContentView: some ToolbarContent {
ToolbarItem(placement: .topBarTrailing) {
Button {
router.navigate(to: .settings)
} label: {
Image(systemName: "gearshape")
}
}
}
}
struct SettingsView: View {
@EnvironmentObject
private var router: Router<ContentRoute>
var body: some View {
Button("Go back to Root") {
router.navigateToRoot()
}
}
}
The RoutingView
essentially is just wrapping your view with a NavigationStack
& navigationDestination
.
NavigationStack(path: $router.stack) {
rootView(router)
.navigationDestination(for: Router<Routes>.Destination.self) { route in
route.body.environmentObject(router)
}
}