SwiftUI : NavigationStack in new iOS 18 TabView pushes twice when path in parameter

Hello,

With iOS 18, when NavigationStack is in new TabView, with path parameter containing current navigation state is set, the navigation destination view is pushed twice.

See below with example that pushes twice on iOS 18 but is correct on iOS 17

@MainActor
class NavigationModel: ObservableObject {
    static let shared = NavigationModel()
    
    @Published var selectedTab: String
    @Published var homePath: [Route]
    @Published var testPath: [Route]
}


struct ContentView: View {
    @StateObject private var navigationModel: NavigationModel = NavigationModel.shared
    
    var body: some View {
        TabView(selection: $navigationModel.selectedTab){
            HomeView()
                .tabItem {
                    Label("Home", systemImage: "house")
                }
                .tag("home")
            
            TestView()
                .tabItem {
                    Label("Test", systemImage: "circle")
                }
                .tag("test")
        }
    }
}


struct HomeView: View {
    @StateObject private var navigationModel: NavigationModel = NavigationModel.shared

    var body: some View {
        NavigationStack(path: $navigationModel.homePath){
            VStack{
                Text("home")
                NavigationLink(value: Route.test1("test1")){
                    Text("Go to test1")
                }
            }
            .navigationDestination(for: Route.self){ route in
                NavigationModelBuilder.findFinalDestination(route:route)
            }
        }
    }
}

I don't what causes the issue because it works well on iOS 16 and iOS 17. I think the path is somehow reset but I don't why (maybe by the TabView ?)

Note that the bug only occurs with TabView. Don't really know if it is a TabView bug or if it is on my side.

I filed a feedback with sample project FB14312064

I am seeing the same thing. App that works fine on 17.5 is quite broken on 18. I also notice that after navigating back and forth a few times the whole navigation stack seems corrupted. Old views that you have previously popped re-appear in the wrong context in the navigation stack, and views are not completely drawn

Thanks for submitting a bug report @brebispanique.

While the team looks into the Feedback report, Trying making these changes to your sample project and let me know if you're still able to reproduce the issue:

  • You could also use the Route enum instance itself for , since it's hashable and get rid of the custom implementation.
enum Route: Hashable {
    case test1(String)
    case test2(String)
    case empty(String)
    
    var id: Self {
        self
    }
}
  • HomeView and TestView should use ObservedObject property wrapper since you are passing a StateObject into a subview.
struct TestView: View {
    @ObservedObject var navigationModel: NavigationModel
    ......

}


struct HomeView: View {
    @ObservedObject var navigationModel: NavigationModel
.....
    }

I'll also suggest you consider migrating from the Observable Object protocol to the Observable macro as well.

I have found a workaround using NavigationPath instead of an array of Route in my NavigationModel

class NavigationModel: ObservableObject {
    static let shared = NavigationModel()
    
    @Published var selectedTab: String
    @Published var homePath: NavigationPath
    @Published var testPath: NavigationPath
}

Edit: It fixes the "push twice" issue but NavigationStack still acts weirdly, even though the path is correct, some views are not correctly popped.

Removing the TabView still fixes all issues so I think the TabView is buggy.

I can confirm that everything works correct when using the NavigationStack outside a TabView. Inside a TabView, even if there is only a single tab, exhibits incorrect behaviour

I'm also facing this issue. I didn't know the TabView was what was causing it. Thought I was going crazy. @DTS Engineer When do you think this could be fixed?

I also face similar issue and wrote about it in another post: https://developer.apple.com/forums/thread/760041

I can confirm that the usage NavigationPath fixes this issue but the other issues appear. A views that weren't pushed appear in path and I have to pop the several times. So it seems that the combination TabView + NavigationStack inside is buggy.

Two notes:

  1. I tried to use iOS 17+ @Observable approach. It didn’t help.
  2. Using @State var path: [RouterDestination] = [] directly inside View seems to help. But it is not what I want as I need this property to be @Published and located inside custom Router class where I can get an access to it, and use for programmatic navigation if needed.
SwiftUI : NavigationStack in new iOS 18 TabView pushes twice when path in parameter
 
 
Q