import SwiftUI
enum Route: Hashable {
case postDetail(id: Int)
case userProfile(id: Int)
case settings
case editPost(id: Int)
}
@MainActor
class Router: ObservableObject {
@Published var path = NavigationPath()
func navigate(to route: Route) {
path.append(route)
}
func navigateBack() {
path.removeLast()
}
func navigateToRoot() {
path.removeLast(path.count)
}
func replace(with route: Route) {
path.removeLast()
path.append(route)
}
}
import SwiftUI
struct ContentView: View {
@StateObject private var router = Router()
var body: some View {
NavigationStack(path: $router.path) {
PostsListView()
.navigationDestination(for: Route.self) { route in
destinationView(for: route)
}
}
.environmentObject(router)
}
@ViewBuilder
private func destinationView(for route: Route) -> some View {
switch route {
case .postDetail(let id):
PostDetailView(postId: id)
case .userProfile(let id):
UserProfileView(userId: id)
case .settings:
SettingsView()
case .editPost(let id):
EditPostView(postId: id)
}
}
}
// Usage in child views
struct PostsListView: View {
@EnvironmentObject private var router: Router
@StateObject private var viewModel = PostsListViewModel()
var body: some View {
List(viewModel.posts) { post in
Button {
router.navigate(to: .postDetail(id: post.id))
} label: {
PostRowView(post: post)
}
}
.navigationTitle("Posts")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
router.navigate(to: .settings)
} label: {
Image(systemName: "gearshape")
}
}
}
}
}
SwiftUI's NavigationStack introduced in iOS 16 provides programmatic navigation with type-safe paths. I use NavigationPath to manage navigation state, enabling deep linking and state restoration. The navigationDestination modifier maps path values to destination views. For complex navigation, I create a Router class that owns the NavigationPath and exposes methods to push/pop. This centralizes navigation logic and makes it testable. Tab-based apps combine TabView with NavigationStack per tab. For backwards compatibility with iOS 15, I use NavigationView with isActive bindings, though NavigationStack is preferred for iOS 16+.