
SwiftUINavigationRouter
is a lightweight protocol designed to simplify and structure navigation logic in SwiftUI using NavigationStack
and NavigationPath
. It provides a scalable and reusable way to manage navigation per feature or flow in your app.
- π Decoupled navigation logic
- π Type-safe and testable
- π§ Declarative API
- π Supports
push
,pop
, andpopToRoot
out of the box - π§± Each flow can have its own router
- π οΈ Ready for dependency injection
- π² Example App Included
- π¦ Future support for
.present
navigation
In Xcode, open your project and go to:
File > Add Packages...
Then, enter the following URL:
https://github.com/juansanzone/swiftui-navigation-router
You can also add it manually to your Package.swift
:
.package(url: "https://github.com/juansanzone/swiftui-navigation-router.git", from: "1.0.0")
To use the router, youβll need to:
- Create a class that conforms to
SwiftUINavigationRouterProtocol
- Define your
Destination
enum - Implement the
viewFor(for:)
method - Connect everything inside a
NavigationStack
in your root view
import SwiftUI
import SwiftUINavigationRouter
final class PaymentsRouter: SwiftUINavigationRouterProtocol {
@Published var navigationPath: NavigationPath = .init()
}
extension PaymentsRouter {
enum Destination: Hashable {
case detailView
case successView(paymentID: String)
}
}
extension PaymentsRouter {
@ViewBuilder
func viewFor(for destination: Destination) -> some View {
switch destination {
case .detailView:
DetailView()
case .successView(let paymentID):
Text("Success View: \(paymentID)")
}
}
}
β Each router manages the navigation logic of a specific flow (e.g., Payments, Profile, Onboarding, etc).
import SwiftUI
struct HomeView: View {
@StateObject private var router: PaymentsRouter = .init()
var body: some View {
NavigationStack(path: $router.navigationPath) {
VStack {
Button("Details") {
router.push(screen: .detailView)
}
}
.navigationDestination(for: PaymentsRouter.Destination.self) { destination in
router.viewFor(for: destination)
.environmentObject(router)
}
.navigationTitle("HomeView")
}
}
}
β οΈ In this example, we use@EnvironmentObject
for simplicity. In real-world apps, prefer a proper dependency injection strategy (e.g., via factory, container, or constructor injection).
import SwiftUI
struct DetailView: View {
@EnvironmentObject var router: PaymentsRouter
var body: some View {
VStack {
Text("DetailView")
.onTapGesture {
router.push(screen: .successView(paymentID: "123"))
}
}
}
}
Pushes a new screen defined in your Destination
enum:
router.push(screen: .detailView)
Pushes a view directly to the NavigationPath
:
router.push("AnyHashableView")
Note: This method is less type-safe. Prefer using
.push(screen:)
with an enum when possible.
Pops the last view in the stack:
router.pop()
Clears the entire navigation stack and returns to the root:
router.popToRoot()
Each major flow (e.g., Payments, Orders, Onboarding) should have its own router. This promotes separation of concerns and improves scalability and testability.
final class OnboardingRouter: SwiftUINavigationRouterProtocol { ... }
final class OrdersRouter: SwiftUINavigationRouterProtocol { ... }
Bored and tired of repeatedly defining navigationDestination
everywhere? Let NavigationRouterView
handle everything for you!
- β Simplified Setup: Define your navigation logic once and reuse it effortlessly across your app.
- β
Cleaner Code: No more repetitive
navigationDestination
setups cluttering your SwiftUI views. - β Easy Maintenance: Centralized routing makes your navigation easy to maintain and scale.
NavigationRouterView
uses a simple yet powerful protocol NavigationRouterProtocol
, which lets you define navigation paths and corresponding views neatly in one place.
Simplify your view by embedding it within NavigationRouterView
:
struct HomeView: View {
@StateObject private var router = MyRouter()
var body: some View {
NavigationRouterView(router: router) {
VStack {
Button("Push Detail View") {
router.push(screen: .detailView)
}
}
.navigationTitle("Home")
}
}
}
That's it! π Now your navigation is cleaner, modular, and easier than ever!
A basic working example is available inside the ExampleApp
folder.
This mini app demonstrates how to:
- Create a router for a feature flow
- Push and pop views using the router
- Display views based on enum-driven navigation
You can open the folder with Xcode and run it on the simulator to see SwiftUINavigationRouter
in action as a local package.
- Support for push-based navigation
- Built-in support for
pop
andpopToRoot
- Sample app
- Automatic Injection: The router is automatically injected into your stack views using SwiftUI's EnvironmentObject.
- Full integration with dependency injection containers
-
present
anddismiss
API (coming soon)
Pull requests are welcome! Feel free to fork the repo, open issues, and help shape the future of SwiftUINavigationRouter
.
MIT License
Created with β€οΈ by Juan Sanzone