Navigation Issue in iOS 18: Duplication of Navigation Trigger When Using @Environment(\.dismiss) in SwiftUI

I’m encountering an issue with SwiftUI navigation in iOS 18, where navigating to a DetailView causes unexpected duplication of navigation behavior when @Environment(.dismiss) is used.

Code Example: Here’s a simplified version of the code:

struct ContentView: View {
    
    var body: some View {
        
        NavigationStack {
            NavigationLink("Go to Detail View", destination: DetailView())
                .padding()
        }
    }
}

struct DetailView: View {
    
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        
        VStack {
            let _ = print("DetailView")  // This print statement is triggered twice in iOS 18
        }
    }
}

Issue:

  • In iOS 18, when @Environment(.dismiss) is used in DetailView, the print("DetailView") statement is triggered twice.

  • The same code works correctly in iOS 17 and earlier, where the print statement is only triggered once, as expected.

  • However, when I remove @Environment(.dismiss) from DetailView, the code works as intended in iOS 18, with the print statement being triggered only once and no duplication of navigation behavior.

Alternative Approach with .navigationDestination(for:): I also tested using .navigationDestination(for:) to handle navigation:

struct ContentView: View {
    
    var body: some View {
        
        NavigationStack {
            NavigationLink("Go to Detail View", destination: DetailView())
                .padding()
        }
    }
}

struct DetailView: View {
    
    @Environment(\.dismiss) var dismiss
    
    var body: some View {
        
        VStack {
            let _ = print("DetailView")  // This print statement is triggered twice in iOS 18
        }
    }
}

Even with this alternative approach, the issue persists in iOS 18, where the print statement is triggered twice.

What I've Tried:

  • I’ve confirmed that removing @Environment(.dismiss) solves the issue, and the print statement is triggered only once, and the navigation works as expected in iOS 18 without duplication.

  • The issue only occurs when @Environment(.dismiss) is in use, which seems to be tied to the navigation stack behavior. The code works correctly in iOS 17 and below, where the print statement is only called once.

Expected Behavior:

  • I expect the print("DetailView") statement to be called once when navigating to DetailView, and that the navigation happens only once without duplication. The presence of @Environment(.dismiss) should not cause the navigation to be triggered multiple times.

Questions:

  • Is this a known issue with iOS 18 and SwiftUI navigation? Specifically, is there a new behavior that interacts differently with @Environment(.dismiss)?

  • Has anyone else encountered this problem, and if so, what’s the recommended way to handle it in iOS 18?

  • Is there a workaround to ensure that the navigation doesn’t trigger more than once when using @Environment(.dismiss) in iOS 18?

Any help or insights would be greatly appreciated!

Thanks for flagging this, this looks like a bug to me. Adding let _ = Self._printChanges() in the view body provides a better explanation as to what's going on here. In essence dismiss is causing a re-render of the SwiftUI view because of an updated value.

It doesn't cause duplicate navigation events but it still could be a performance issue. See Demystify SwiftUI performance to learn more about common causes behind performance issues

Please open a bug report, include your findings and the test environment. Please post the FB number here once you do.Bug Reporting: How and Why? has tips on creating your bug report.

An alternative approach you could consider is to programmatically manage your navigation using a path input.

Please review the following resources to learn about programmatic navigation:

Navigation Issue in iOS 18: Duplication of Navigation Trigger When Using @Environment(\.dismiss) in SwiftUI
 
 
Q