SwiftUI - NavigationLink inside NavigationBarItems, returns error on navigating from detail back to master view

Here's the gist of the problem: I have a NavigationLink inside the .navigationBarItems(trailing: LinkHere {ImageHere }) property for a view. When I tap the link, it directs to the new view just fine. However, when I tapthe back button, the app throws the following error, along with a signal abort error on the AppDelegate class line:


2019-10-22 12:17:55.403091-0700 MyApp[5203:116164] *** Assertion failure in -[UINavigationController popToViewController:transition:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKitCore_Sim/UIKit-3900.12.2.1/UINavigationController.m:8129

2019-10-22 12:17:55.412311-0700 MyApp[5203:116164] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Tried to pop to a view controller that doesn't exist.'

*** First throw call stack:

(

0 CoreFoundation 0x00007fff23c0b02e __exceptionPreprocess + 350

1 libobjc.A.dylib 0x00007fff50b00b20 objc_exception_throw + 48

2 CoreFoundation 0x00007fff23c0ada8 +[NSException raise:format:arguments:] + 88

3 Foundation 0x00007fff25684b61 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191

4 UIKitCore 0x00007fff470ab08d __57-[UINavigationController popToViewController:transition:]_block_invoke + 620

5 UIKitCore 0x00007fff470aad3a -[UINavigationController popToViewController:transition:] + 753

6 SwiftUI 0x00007fff2c0cb7dd $s7SwiftUI21UIKitNavigationBridgeC3pop33_F345616596EA75D1F4200D7666E5E588LL8animatedySb_tF + 413

7 SwiftUI 0x00007fff2c0caaa3 $s7SwiftUI21UIKitNavigationBridgeC20preferencesDidChangeyyAA14PreferenceListVF + 1267

8 SwiftUI 0x00007fff2c05691d $s7SwiftUI14_UIHostingViewC20preferencesDidChangeyyF + 477

9 SwiftUI 0x00007fff2c1555dd $s7SwiftUI9ViewGraphC13updateOutputs2atyAA4TimeV_tF + 221

10 SwiftUI 0x00007fff2c4a8629 $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtFyyXEfU_yyXEfU_ + 1001

11 SwiftUI 0x00007fff2c4a803a $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtFyyXEfU_ + 634

12 SwiftUI 0x00007fff2c49c094 $s7SwiftUI16ViewRendererHostPAAE6render8interval17updateDisplayListySd_SbtF + 436

13 SwiftUI 0x00007fff2c637e42 $s7SwiftUI14_UIHostingViewC14layoutSubviewsyyF + 226

14 SwiftUI 0x00007fff2c637e65 $s7SwiftUI14_UIHostingViewC14layoutSubviewsyyFTo + 21

15 UIKitCore 0x00007fff47ca0dc5 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2478

16 QuartzCore 0x00007fff2b0f3db1 -[CALayer layoutSublayers] + 255

17 QuartzCore 0x00007fff2b0f9fa3 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 517

18 QuartzCore 0x00007fff2b1058da _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 80

19 QuartzCore 0x00007fff2b04c838 _ZN2CA7Context18commit_transactionEPNS_11TransactionEd + 324

20 QuartzCore 0x00007fff2b081b41 _ZN2CA11Transaction6commitEv + 643

21 QuartzCore 0x00007fff2b0824aa _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv + 76

22 CoreFoundation 0x00007fff23b6d617 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23

23 CoreFoundation 0x00007fff23b680ae __CFRunLoopDoObservers + 430

24 CoreFoundation 0x00007fff23b6872a __CFRunLoopRun + 1514

25 CoreFoundation 0x00007fff23b67e16 CFRunLoopRunSpecific + 438

26 GraphicsServices 0x00007fff383d1bb0 GSEventRunModal + 65

27 UIKitCore 0x00007fff477bcef8 UIApplicationMain + 1621

28 MyApp 0x000000010fd07e0b main + 75

29 libdyld.dylib 0x00007fff51986c39 start + 1

)

libc++abi.dylib: terminating with uncaught exception of type NSException

(lldb)


The following StackOverflow link has the same problem for me, and the code is, in essence, the same:
https://stackoverflow.com/questions/58404725/why-does-my-swiftui-app-crash-when-navigating-backwards-after-placing-a-navigat

Has there been any solution for this? Is it really just a bug?

Replies

I suspect the push controller is looking at the nav link and getting its closest ancestor UIViewController, expecting that to be the view controller assigned inside the navigation controller. However, the closest controller to the actual bar is the nav bar controller itself, so that's why it selects the wrong thing there.

Any update on this item? Should NavigationLink's not be put into the Navigation bar? Or is there a bug in IOS here?

This reproduces the problem:


struct ContentView: View {
    @State var choices = ["Red", "Green", "Blue"]
    
    var body: some View {
        NavigationView {
            List(choices, id: \.self) { item in
                NavigationLink(destination: SecondView()) {
                    Text(item)
                }
            }
            .navigationBarTitle("First")
            .navigationBarItems(
                trailing:
                    NavigationLink(destination: SecondView()) {
                        Text("Add")
                    }
            )
        }
    }
}


It doesn't matter what SecondView is. The point is that the nav bar trailing item, a NavigationLink, is inside the NavigationView. When this "Add" NavigationLink is tapped, it goes to the SecondView. When the Back button is tapped, the crash happens as described above. This seems a very typical way to display a list and add items to the list without using a presentation sheet.

For the record, there are several shipping apps from Apple that use SwiftUI. Off the top of my head, the watchOS calculator and compass apps are two of them.

My bug is now showing > 10 duplicates and a resolution of “Potential fix identified - For a future OS update,” which likely means there will be a fix in an upcoming point release.

I've been banging my head against this for a while and found this thread. I am also having an identical issue to everyone here, but haven't been able to establish a workaround. I installed Xcode Beta 11.3 (11C24b) to determine if the issue is resolved (the latest at the time of writing), but this has not fixed the issue when running my app in a simulator. I guess we just play the waiting game for now? 😬

Having the same problem here.


It seems that if you use the same idea of this answer in stackoverflow, it works.


https://stackoverflow.com/a/57837007/3377161


Basically you add a Button in your NavigationBar and then inside the body the navigationlink.

As for my case, putting navigationBarTitle after navigationBarItems can reslove this bug.


Update: this cannot fix this bug.

On 13.3 this no longer crashes and will return to the root view when back is tapped, but now I am not longer able to navigate back to the chold view. So no crash good, still a broken UI bad.


I've also tried other suggestions with NavigationLink and displaying with the isActive property, but the same result happen where I end up on the root view and can never return to the child view.


Quick update... The default Master / Detail Projedct Template from xCode suffers from this issue. I get this is a new technology, but when the default code prodived by the IDE has the issue it makes me wonder if there is any QA process. If there is only one row in the table the issue happens, but if you add two rows then the app behaves as you'd expect.

I'm seeing this too. 13.3 fixed the back nav crash but has broken future navigations. Additionally, this broken behavior applies to the previous workaround of an invisible link triggered by a button in the same way. So programmatic navigation seems completely broken right now.

My workaround is pretty horrible where the child basically forces the parent to repaint. This introduces other small issues if the navigation is triggered via a link.


Since this issue happens with a brand new project with no modifications to the Master / Detail template I am hopeful it could get addressed quickly.

Interesting to note that those are watchOS apps.


Effectively, I understand SwiftUI makes a lot of sense of watch apps where storyboards are not really useful.

Eh, I only know about those two because I was on the watchOS team once upon a time, and know who to ask now 😉