In my SwiftUI app I'm using NavigationStack with navigationDestination modifier to navigate user between views. In my AppState I'm holding a state for array of NavigationPath that are triggering state in my ContentView with Combine's Publisher:
struct NavigationState: Equatable {
var path: [NavigationPath] = []
}
enum NavigationPath: Hashable {
case view1
case view2
case view3
case view4
}
To navigate between views I have a NavigationState extension. These are the methods I call on appState to change path array:
extension AppState.NavigationState {
mutating func navigate(to destination: AppState.NavigationPath) {
if !path.contains(destination) {
path.append(destination)
}
}
mutating func pop() {
_ = path.popLast()
}
mutating func popToView2() {
path = [.view2]
}
}
This is how the ContentView looks like:
struct ContentView: View {
@State private var navigationPath: [AppState.NavigationPath] = [.view1]
var body: some View {
NavigationStack(path: $navigationPath) {
View1()
.navigationDestination(for: AppState.NavigationPath.self) { path in
switch path {
case .view1:
View1()
case .view2:
View2()
case .view3:
View3()
case .view4:
View4()
}
}
}
.onReceive(navigationPathUpdate) {
self.navigationPath = $0
}
}
}
// MARK: - State Update
private extension ContentView {
var navigationPathUpdate: AnyPublisher<[AppState.NavigationPath], Never> {
container.appState.updates(for: \.navigationState.path)
}
}
updates method is just for updating the state and I guess it's not important to explain in detail.
In the end the code works perfect on all iPhones and in all iOS versions except iOS 17.1.2 (tester's iPhone 14 Pro). When the user calls popToView2 app crashes. I've tried to comment out this call and in that case app is not crashing for the tester, so the only guess left is that popToView2 does something bad. I have a crash log in Xcode but it doesn't point the exact line of code where the problem is. popToView2 is used after log out to show login screen again.

Did anyone experience something similar?
You are supposed to use multiple
navigationDestinationmodifiers for each type of value, not a single modifier with an enum/switch inside. These are designed to be declared throughout yourView(model) struct hierarchy. The path has been specially designed to be able to hold different types unlike array.