Router is designed for applications using a TabView where each tab maintains its own independent navigation stack.
Implementation Steps:
- Define Tab Type: Create an enum conforming to
TabType. It must be CaseIterable and Identifiable. - Define Destination and Sheet Types: Create enums conforming to
DestinationType and SheetType. - Initialize Router: Create a
@State instance of Router<AppTab, Destination, Sheet>(initialTab: .home). - Configure SwiftUI Views:
- Bind
TabView(selection:) to router.selectedTab. - For each tab, create a
NavigationStack(path:) bound to router[tab] (the specific path for that tab). - Bind
.sheet(item:) to router.presentedSheet.
- Navigate: Use the router to navigate within a specific tab using
.navigateTo(_:for:).
Example
import SwiftUI
import AppRouter
enum AppTab: String, TabType, CaseIterable {
case home, profile, settings
var id: String { rawValue }
var icon: String { "house" }
}
enum Destination: DestinationType {
case detail(id: String)
case list
}
enum Sheet: SheetType {
case settings
var id: Int { hashValue }
}
struct ContentView: View {
@State private var router = Router<AppTab, Destination, Sheet>(initialTab: .home)
var body: some View {
TabView(selection: $router.selectedTab) {
ForEach(AppTab.allCases) {
NavigationStack(path: $router[tab]) {
HomeView()
.navigationDestination(for: Destination.self) { dest in
// Handle destination
}
}
.tabItem { Label(tab.rawValue, systemImage: tab.icon) }
.tag(tab)
}
}
.sheet(item: $router.presentedSheet) { sheet in
// Handle sheet
}
}
}
import SwiftUI
import AppRouter
enum AppTab: String, TabType, CaseIterable {
case home, profile, settings
var id: String { rawValue }
var icon: String {
switch self {
case .home: return "house"
case .profile: return "person"
case .settings: return "gear"
}
}
}
enum Destination: DestinationType {
case detail(id: String)
case list
case profile(userId: String)
}
enum Sheet: SheetType {
case settings
case profile
case compose
var id: Int { hashValue }
}
struct ContentView: View {
@State private var router = Router<AppTab, Destination, Sheet>(initialTab: .home)
var body: some View {
TabView(selection: $router.selectedTab) {
ForEach(AppTab.allCases) { tab in
NavigationStack(path: $router[tab]) {
HomeView()
.navigationDestination(for: Destination.self) { destination in
destinationView(for: destination)
}
}
.tabItem {
Label(tab.rawValue.capitalized, systemImage: tab.icon)
}
.tag(tab)
}
}
.sheet(item: $router.presentedSheet) { sheet in
sheetView(for: sheet)
}
}
@ViewBuilder
private func destinationView(for destination: Destination) -> some View {
switch destination {
case .detail(let id):
Text("Detail \(id)")
case .list:
Text("List")
case .profile(let userId):
Text("Profile \(userId)")
}
}
@ViewBuilder
private func sheetView(for sheet: Sheet) -> some View {
switch sheet {
case .settings:
Text("Settings")
case .profile:
Text("Profile Sheet")
case .compose:
Text("Compose")
}
}
}