14.5 beta3 NavigationLink unexpected pop

Make sure to report the issue to Apple using Feedback if you want any chance to see it fixed. (Feedback currently seems to be down.)

Seeing the same issue on a fairly small application, for me it was happening with a similar setup as OP.

Exactly two navigation links on a screen using two distinct elements of the state.

Temp fix by adding a hidden NavigationLink :\ - feedback submitted.

Hey folks, I was able to reliably reproduce the issue without using TCA aka vanilla SwiftUI (I've update my repo).
I narrowed it down to 2 main cases (so far):

  1. Updating an @State property only when that property is referenced anywhere in the view hierarchy.
  2. Updating an @ObservedObject / @StateObject property even when it is not referenced in the view hierarchy.

Ensuring the View does not have exactly 2 NavigationLinks works around the issue in both cases.

We are seeing this behavior quite frequently using TCA because of the design of composing states and its use of @ObservedObject for the ViewStore.

Has there been any fix to this?

My macOS app now keeps showing "Unable to present" when clicking on a NavigationLink and the child view never opens. The app is essentially unusable after the Big Sur 11.3 / Xcode 12.5 update.

Surely many others are impacted?

I tried the EmptyView() fix to no avail.

I also had exactly this problem.

Above gist is a simple code for my problem situation.

And the gist below is the code of the way I solved the problem right away.

I don't know why this issue happen. but still occurred in iOS 14.5, iOS 14.6

If you have better solution, please let me know.

I ran you code and there is no problem. Xcode Version 12.5 (12E262)

Your used of Groups doesn't make sense. The Text("Secondary") is not shown on screen. All should be just inside one VStack

@young Thanks for running my code sample. Are you running it in an iPad where both views of the NavigationView (primary and secondary) are in effect and displayed? If on the iPhone it works fine—the bug appears when the Secondary is displayed because, as I mentioned, "This uses the split-screen feature of NavigationView (supplying two views)." I agree a VStack is better but it's beside the point and changing to a VStack does not fix the problem. I just re-ran it in XCode 12.5 (12E262). If you try it in iPad, please let me know if the bug exhibits or not.

I was running in. iPhone. In iPad does show problem:

So it shows blank screen with "< Back" button on start up and NavLink only navigate once, after none work. Don't see "Unable to present. Please file a bug."

1 Like

Fixed in XCode 13 Beta / iPadOS 15 simulator! My code sample from April 28 (above: using NavigationLink in side-by-side 2-panel iPad view, with no state at all), which was broken in XCode 12 with iOS 14.5 now works well in XCode 13 Beta (running in the iPadOS 15 simulator). Good enough for me!

2 Likes

Thanks man I had the same issue with that on none beta version
it fixed my issue of unexpected pop on @EnvironmentObject value change.

i think the bug is still in it.
but with @EnvironmentObject

my screen transition was
Scanner to confirmation to login to home to login to scanner to confirmation to login to home and in home I tried to change @EnvironmentObject and it poped me to Scanner screen instead just new pop up

In case folks were looking for more concrete examples of how to fix the problem in TCA, a discussion was opened on GitHub recently: Multiple NavigationLink with composable architecture · Discussion #670 · pointfreeco/swift-composable-architecture · GitHub

1 Like

I too have found that the bounce-back behavior is gone in Xcode 13 Beta/iOS 15 simulator. Happy to have found that! But frustrating to have to wait.

One more note.. in Xcode 12.5.1, I've found the culprit to be .navigationViewStyle(StackNavigationViewStyle()

With this enabled on the Top Level NavigationView, the second level navigation works, but the third level bounces back.
With this disabled on the Top Level NavigationView, the second level navigation works once, then I tap into the third level which works, but when I go back to the second level, I cannot tap onto any other items -- I have to first go back to the first level navigation, and then back into the second level.

TLDR: The problem happens with just 2 navigations.
Adding a third navigation stops the issue.

In my case I had:

(1) A tab view with a root view that had 2 navigation starting points

(2) the first and second navigations have quite a lot of nested views

(3) adding .navigationViewStyle(StackNavigationViewStyle() fixed the issue only for root views that had only one starting point of navigation

(4) as soon as I had another navigation coming from the same view the problem reappeared (only for iOS 14.5 to 14.8)

(5) solely adding the

NavigationLink(destination: EmptyView()) {
    EmptyView()
}

didn't work for me

(6) On my project I have a coordinator that is responsible for creating the viewModels (that are published properties) and I have a ContainerView that will handle all the navigation. The navigation links are created based on the existence or not of a viewModel, so if a viewModel exists that view will be presented and when the view is dismissed the viewModel will be set to nil. (I'll add the code that does that at the end)

(7) for some weird reason, adding a third navigation to the view that is responsible for the navigation stopped my container view to be re-rendered and the view to stop being popped back.

Simply adding the NavigationLink to the container didn't work, but using the modifier I'm using and setting the destination to be the Empty NavigationLink did.

This is the code that is used for the navigation:

    func navigation<Item, Destination: View>(
        item: Binding<Item?>,
        @ViewBuilder destination: (Item) -> Destination
    ) -> some View {
        let isActive = Binding(
            get: { item.wrappedValue != nil },
            set: { value in
                if !value {
                    item.wrappedValue = nil
                }
            }
        )
        return navigation(isActive: isActive) {
            item.wrappedValue.map(destination)
        }
    }

    func navigation<Destination: View>(
        isActive: Binding<Bool>,
        @ViewBuilder destination: () -> Destination
    ) -> some View {
        overlay(
            NavigationLink(
                destination: isActive.wrappedValue ? destination() : nil,
                isActive: isActive,
                label: { EmptyView() }
            )
        )
    }

This is how my container view works. My coordinator has Published properties that are optional viewModels and the existence or not of that viewModel is what triggers the isActive value on the navigation link. Adding that last .navigation worked. My empty container just has the empty navigationLink

Not the greatest first post: you should remove your "S-comment" above as it doesn't really help (and I would edit my post to remove your quote once you do so). If you provide a minimum sample app (obviously without any KMM stuff if you want anyone to look at it here) I can test it to see if I can reproduce your results. I've seen unexpected pops and other navigation view / modal sheets issues, but mainly those were on iOS 15. Note that you are posting your comment into a topic called "14.5 beta3".

I'm getting some errors with your approach using the latest Swift version (5.5 I think):

(328, 14) no exact matches in call to initializer

the error happens in this line:

self.init(body: NavigationLinkImpl(destination: destination, label: label))

I could never find a reliable solution to this horrible bug. So I decided to create a custom NavigationLink. This works way better than expected, because all swiftui related functions continue working as usual. Seems like the bug is specifically with NavigationLink.

what xcode version? I am using 13.2.1 and not getting this error. Maybe you have an extension of NavigationLink somewhere?

I came across this problem too, and my fix was using a custom NavLink and changing @EnvironmentMode(\.presentationMode) to @EnvironmentMode(\.dismiss)

You can only have one detail NavigationLink. If you want more levels you need to set isDetailLink(false).