- Blog/
A SwiftUI Router
app
programming
Swift
Talk Dim Sum
One update I made to Talk Dim Sum this year was to add a custom router so I can escape the NavigationLink straightjacket.
My router is just a simple stack.
@Observable
public class Router<T: Equatable> {
public var path: [T] = []
public init() {
}
public func push(_ route: T) {
path.append(route)
}
public func pushNew(_ route: T) {
if path.last != route {
push(route)
}
}
public func pop() {
if !path.isEmpty {
path.removeLast()
}
}
func clear() {
path = []
}
}
It’s generic so I can use it in more than one app, each with a different set of routes. Here’s the one for my dim sum app, just listing the routes for bringing up a camera view and for displaying the image taken.
enum Route: Hashable {
case mlCamera
case mlImage(UIImage)
}
The enum cases have to be mapped to actual views.
struct DestinationView: View {
let route: Route
var body: some View {
switch route {
case .mlCamera:
MLCameraView()
case let .mlImage(image):
MLImageView(image: image)
}
}
}
Now I can instantiate the router at the top level of my app and pass it down as an environment object.
@State var router = Router<Route>()
var body: some Scene {
WindowGroup {
AppView()
.environment(router)
}
}
First supplying it to the NavigationStack, and within that establish the navigation destination.
@Environment(Router<Route>.self) var router: Router<Route>
var body: some View {
@Bindable var router = router
NavigationStack(path: $router.path) {
Tabs()
.navigationDestination(for: Route.self) { route in
DestinationView(route: route)
}
}
}
Now it’s ready to use, e.g. this line pushes a new view on the stack (if it’s not already there)
router.pushNew(.mlImage(image))