Navigation patterns with NavigationStack

Sofia Martinez Jan 2026
2 tabs
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)
    }
}
2 files · swift Explain with highlit

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+.