Routing
is a library for separating navigation logic from SwiftUI views.
- De-couples navigation logic from SwiftUI views.
- Leads to better separation of concerns.
- Requirements
- Installation
- Getting Started
- Deep Linking Support
- Additional Resources
- Usage With TabView
- Requires iOS 16 or later.
You can install Routing
using the Swift Package Manager.
- In Xcode, select "File" > "Add Packages...".
- Copy & paste the following into the "Search or Enter Package URL" search bar.
https://github.com/obvios/Routing.git
- Xcode will fetch the repository & the "Routing" library will be added to your project.
- Create a
Routable
conformingEnum
to represent the different views you wish to route to.
import SwiftUI
import Routing
enum ExampleRoute: Routable {
case viewA
case viewB(String)
case viewC
@ViewBuilder
func viewToDisplay(router: Router<ExampleRoute>) -> some View {
switch self {
case .viewA:
ViewA(router: router)
case .viewB(let description):
ViewB(router: router, description: description)
case .viewC:
ViewC(router: router)
}
}
var navigationType: NavigationType {
switch self {
case .viewA:
return .push
case .viewB(_):
return .sheet
case .viewC:
return .fullScreenCover
}
}
}
- Wrap your view hierarchy in a
RoutingView
that is initialized with yourRoutable
enum. It will inject aRouter
instance into your root view.
import SwiftUI
import Routing
struct ContentView: View {
var body: some View {
RoutingView(ExampleRoute.self) { router in
RootView(router: router)
}
}
}
struct RootView: View {
@StateObject var router: Router<ExampleRoute>
init(router: Router<ExampleRoute>) {
_router = StateObject(wrappedValue: router)
}
var body: some View {
VStack() {
Button("View A") {
router.routeTo(.viewA)
}
Button("View B") {
router.routeTo(.viewB("Got here from RootView"))
}
Button("View C") {
router.routeTo(.viewC)
}
}
}
}
- Use the
Router
functions from any of your views. Here isViewA
which is pushed onto the navigation stack byRootView
.
struct ViewA: View {
@StateObject var router: Router<ExampleRoute>
init(router: Router<ExampleRoute>) {
_router = StateObject(wrappedValue: router)
}
var body: some View {
Text("View A")
Button("ViewC") {
router.routeTo(.viewC)
}
Button("Dismiss") {
router.dismiss()
}
}
}
Routing
provides support for deep linking using the .onDeepLink(using:_:)
modifier. This allows clients to handle incoming URLs and navigate to the appropriate Routable
destination. NOTE: The library will dismiss any sheets/fullScreenCovers automatically if needed.
Attach .onDeepLink(using:_:)
to any view inside RoutingView
to handle deep links:
import Routing
struct ContentView: View {
var body: some View {
RoutingView(ExampleRoute.self) { router in
RootView(router: router)
.onDeepLink(using: router) { url in
// Add your logic to handle deeplink here
print(url)
// Return the destination to navigate to for the deeplink
return ExampleRoute.viewC
}
}
}
}
The below articles are from my blog series explaining the Router
pattern and documents the progress of this library.
- Learn about the Router pattern for SwiftUI navigation
- See how presentation was added
- Blog post explaining this Routing library
- Using the Router in Different Tabs: Each tab should embed its own
RoutingView
instance to manage navigation independently within that tab.
To ensure proper navigation behavior in a multi-tab environment, structure your application like this:
import SwiftUI
import Routing
struct MainTabView: View {
var body: some View {
TabView {
RoutingView(ExampleRouteA.self) { router in
RootViewA(router: router)
}
.tabItem {
Label("Tab 1", systemImage: "house")
}
RoutingView(ExampleRouteB.self) { router in
RootViewB(router: router)
}
.tabItem {
Label("Tab 2", systemImage: "gear")
}
}
}
}
NOTE: Currently the Router cannot be used to route to views in a different tab. This limitation is due to the original single flow design of the library. I am working to address this in the next version.