NavigationLink(destination:tag:selection) behaving weirdly in split view

Hi!

I have noticed weird behaviour when using a NavigationLink with tag and selection binding that is displayed in split view.

The full code is here.

I created a NavigationLink like this:

        NavigationLink(
          destination: IfLetStore(
            self.store.scope(state: { $0.selectedNumber }).actionless,
            then: DebugNavigationLinkChildView.init(store:),
            else: Text("selectedNumber is nil!")
          ),
          tag: number,
          selection: viewStore.binding(get: { $0.selectedNumber }, send: {
            print("NavigationLink for \(number) is sending \($0)")
            return ViewAction.selectNumber($0)
          })
        ) {
          Text("Go to \(number)")
        }

What I am observing is that if the view is displayed using a split view and one element is selected and I click another, first the clicked element is sending its tag, then the previously selected element is sending nil.

What this means is that when first selecting 1, then selecting 2 we end up with nil!

This is the debug output when running it on an iPad in landscape:

// Select 1
NavigationLink for 1 is sending Optional(1)

// Select 2
NavigationLink for 2 is sending Optional(2)
NavigationLink for 1 is sending nil
NavigationLink for 2 is sending nil

When running this on an iPhone I get this instead (the view works as expected):

// Select 1
NavigationLink for 1 is sending Optional(1)

// Go back
NavigationLink for 1 is sending nil
NavigationLink for 1 is sending nil

// Select 2
NavigationLink for 2 is sending Optional(2)

So how can I distinguish between a navigation link sending nil because the user went back and because another element is selected?

I guess I could include the sender number as well in the action and then only allow nil when it is coming from the currently selected sender:

    case .selectNumber(let sender, let number):
      if let number = number {
        state.selectedNumber = number
      } else if number == nil && sender == state.selectedNumber {
        state.selectedNumber = nil
      }

But this seems like a hack. Is there a better solution? Am I doing something wrong?

2 Likes

This sounds like another bug around SwiftUI navigation. Previously:

I'd suggest trying to reproduce this behavior outside the Composable Architecture (see the last link for a Binding helper that aids in debugging) and filing Feedback if you can!