I am trying to hide the Navigation path of the NavigationStack into the ViewModel of a view but doing so breaks the NavigationStack, the stack adds the items correctly but it does not updates the UI.
This code shows a simple view with a button that is supposed to navigate to a new view and show the number of elements in the navigation stack.
Is there a way of making it work?
import Foundation
import SwiftUI
@main
struct TestingCoordinatorNavigationApp: App {
var body: some Scene {
WindowGroup {
NavigationView()
}
}
}
enum Destination {
case firstPage
}
class Coordinator: ObservableObject {
@Published var path: [ViewModel] = [] { didSet { debugPrint(path) } }
func goToFirst() {
path.append(ViewModel(number: 1, coordinator: self))
}
}
class NavigationViewModel: ObservableObject {
@Published var coordinator = Coordinator()
}
struct NavigationView: View {
@ObservedObject var viewModel = NavigationViewModel()
// it workd fine if I do this:
// @ObservedObject var coordinator = Coordinator()
// and replace the call in the Button for coordinator.goToFirst()
var body: some View {
NavigationStack(path: $viewModel.coordinator.path) {
VStack {
Button(action: {
viewModel.coordinator.goToFirst()
}) {
Text("Go to first view")
}
.navigationDestination(for: ViewModel.self) { viewModel in
ViewFactory(viewModel: viewModel)
}
}
}
}
}
struct ViewFactory: View {
var viewModel: ViewModel
var body: some View {
switch viewModel.number {
case 1:
ChildView(viewModel: viewModel)
default:
Text("ViewModel not recognized")
}
}
}
struct ChildView: View {
var viewModel: ViewModel
var body: some View {
VStack {
Text("This is a view \(viewModel.number) and there are \(viewModel.coordinator.path.count) views in the stack")
Button {
debugPrint("Push another view in the stack")
viewModel.coordinator.goToFirst()
} label: {
Text("Push another view")
}
}
}
}
class ViewModel {
var number: Int
@ObservedObject var coordinator: Coordinator
init(number: Int, coordinator: Coordinator) {
self.number = number
self.coordinator = coordinator
}
static func == (lhs: ViewModel, rhs: ViewModel) -> Bool {
return lhs.number == rhs.number
}
}
extension ViewModel: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}