NavigationStack + navigationDestination issue in

Hi everyone, this is my first post. I'm facing an issue that I don't fully understand. The problem started once I migrated to use NavigationStack instead of navigation View.

I have a View, let's called "NextWeekView" where I do some async process onAppear, calling an async function from the "NextWeekViewModel". Depending on the result of this processing, I need to navigate automatically to different Views. I use @Published variables on the ViewModel that I populate with the destination view. Once the processing is finish, on the View and still on onAppear, I enable a @State navigate, and return the destination View based on the @Publish:

The View

enum DestinationType {
case play
case menu
case endseason
}
struct NextWeekView: View {
@ObservedObject var team: Team
@StateObject var nextWeekViewModel = NextWeekViewModel()
@State private var destination: DestinationType? = nil
@State var navigate: Bool = false
var body: some View {
LoadingScreenView(text: self.nextWeekViewModel.currentStep)
.navigationDestination(isPresented: self.$navigate) {
destinationView(for: self.nextWeekViewModel.destination)
}
.navigationBarHidden(true)
.statusBar(hidden: true)
.onAppear{
Task {
await nextWeekViewModel.processNextWeek(team: team)
if nextWeekViewModel.finishedProcessing {
self.navigate.toggle()
}
print("navigate to: \(self.navigation)") // debug, it always print the expected value.
}
}
}
// Function to return the appropriate view based on the destination
@ViewBuilder
private func destinationView(for destination: DestinationType?) -> some View {
switch destination {
case .play:
MatchPlayView(team: self.team)
case .menu:
GameMenuView().environmentObject(team)
case .endseason:
SeasonEndView().environmentObject(team)
case .none:
EmptyView()
}
}
}

The ViewModel

class NextWeekViewModel: ObservableObject {
var context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
@Published var currentStep:LocalizedStringKey = ""
@Published var destination: DestinationType? = .menu
@Published var finishedProcessing: Bool = false
@MainActor
func processNextWeek(team: Team) async {
....
if currentWeek.matchdays.filter({ md in return md.num > currentWeek.current_matchday }).count == 0 {
await self.endWeekProcess()
// We check if it is end of season
if currentSeason.current_week == currentSeason.weeks.count && currentWeek.matchdays.filter({ md in return md.num > currentWeek.current_matchday }).count == 0 {
// End of season
await self.endSeasonProcess(team: team, currentSeason: currentSeason)
self.destination = .endseason // WORKS
} else {
// We go next week
self.currentStep = "Create cash operations"
await self.createCashOperations(team: team, week: currentSeason.current_week)
self.currentStep = "Create messages"
await self.createMessages(team: team)
self.destination = .menu //WORKS
}
} else {
print("Ther are more matchdays, lets pass the matchday")
// THIS DOES NOT WORK
// Any Modifications of destination is reflected on the view, but automatic navigation is not taking place
self.destination = .play
//self.destination = .menu // this also do not work.
}
self.finishedProcessing = true
}

What's happens: The code works when app goes through code path marked as //WORKS but not when follows code path //DO NOT WORK. Interestingly, it works perfectly on iOS 18. But not in iOS16/iOS17.

If I add an interactive NavigationLink instead of the navigationDestination in the View :

if self.nextWeekViewModel.finishedProcessing {
NavigationLink(destination:destinationView(for: self.nextWeekViewModel.destination)){
SimpleButtonWithIconView(icon: "chevron.right.2", text: "Next")
.padding(.bottom, 10)
}
}

Then it works as expected but I really want to avoid the user to tap Next for a seamlessly gameplay

I've tried all combinations of state variables, using different state variables per destination, but I always arrive to the same situation.

I also printed everything left and right and View values looks consistent so I think the ViewModel is doing the right thing (also with the NavigationLink it works). It's simply that the "automatic" navigation does not want to navigate in certain occasions.

I'm completely out of ideas. Any feedback will be welcomed. Otherwise I will revert back to NavigationView.

Thank you in advance!

My initial suggestion would be for you to:

  • Migrate to Observation instead of using @ObservedObject and move your logic from the .onAppear modifier to the .task modifier. onAppear/onDisappear are about view lifecycle, and doesn’t equate to view visibility.

  • You could also restructure how func processNextWeek(team: Team) might be better modeled to have it return the Boolean value. The alternatively, is to use onAppear + .onChange modifier to observe nextWeekViewModel.finishedProcessing. This would ensure that your SwiftUI view observes the value and you can then enqueue an update to your Swift state variable.

These are just suggestions and you can give those a try—if you can provide a focused example, it’ll be easier to understand the issue. If you're unfamiliar with creating a test project see, Creating a test project.

Thank you, I will try those approaches and return with the results!

NavigationStack + navigationDestination issue in
 
 
Q