---
title: Implement Pop-to-Root by Clearing NavigationPath
impact: MEDIUM-HIGH
impactDescription: O(1) pop-to-root via path.removeAll(), replaces N manual dismiss calls
tags: flow, pop-to-root, navigation-path, programmatic
---

## Implement Pop-to-Root by Clearing NavigationPath

**Clinic architecture alignment (iOS 26 / Swift 6.2):** Keep Feature modules on `Domain` + `DesignSystem` only; keep App-target `DependencyContainer`, route shells, and concrete coordinators as the integration point; keep `Data` as the only owner of SwiftData/network/sync I/O.

With NavigationStack and a bound path, pop-to-root is simply setting `path = NavigationPath()` or removing all elements. The old UIKit hacks of walking the responder chain, posting NotificationCenter broadcasts, or reaching into UINavigationController internals are fragile, break across OS updates, and couple your views to UIKit implementation details that SwiftUI is designed to abstract away.

**Incorrect (NotificationCenter or UIKit responder chain hacks):**

```swift
// BAD: Broadcasting pop-to-root via NotificationCenter —
// fragile, requires every NavigationStack to observe the
// notification, and breaks when view hierarchy changes
extension Notification.Name {
    static let popToRoot = Notification.Name("popToRoot")
}

struct DeepChildView: View {
    var body: some View {
        Button("Return to Start") {
            // Notification-based workaround — no guarantee the
            // right stack receives it, race conditions possible
            NotificationCenter.default.post(name: .popToRoot, object: nil)
        }
    }
}

struct RootView: View {
    @State private var path = NavigationPath()

    var body: some View {
        NavigationStack(path: $path) {
            HomeView()
        }
        // Must remember to add this observer on every root —
        // easy to forget, impossible to test reliably
        .onReceive(NotificationCenter.default.publisher(for: .popToRoot)) { _ in
            path = NavigationPath()
        }
    }
}
```

**Correct (clear the navigation path directly):**

```swift
// GOOD: Pop-to-root by clearing the path — direct, testable,
// no side channels or UIKit dependencies
@Observable @MainActor
class NavigationCoordinator {
    var path = NavigationPath()

    // Clear and explicit — pop to root in one call
    func popToRoot() {
        path = NavigationPath()
    }

    // Pop a specific number of screens
    func pop(_ count: Int = 1) {
        let removeCount = min(count, path.count)
        path.removeLast(removeCount)
    }
}

@Equatable
struct RootView: View {
    @State private var coordinator = NavigationCoordinator()

    var body: some View {
        NavigationStack(path: $coordinator.path) {
            HomeView()
                .navigationDestination(for: AppRoute.self) { route in
                    route.destination
                }
        }
        .environment(coordinator)
    }
}

@Equatable
struct DeepChildView: View {
    @Environment(NavigationCoordinator.self) private var coordinator

    var body: some View {
        VStack(spacing: 16) {
            Button("Back to Start") {
                // Direct path mutation — no notifications, no hacks
                coordinator.popToRoot()
            }
            Button("Back Two Screens") {
                // Granular control over how far to pop
                coordinator.pop(2)
            }
        }
    }
}
```
