Why is the NavigationStack not navigating?

708 Views Asked by At

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))
    }
}
0

There are 0 best solutions below