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
navigationDestination
modifiers 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.