-1

Suppose I'm working on a calendar library in SwiftUI 4. The calendar has a button for user to select a month. When user clicks on that button, it navigates to a view which lists all the months for user to select. The key setup here is the calendar needs to navigate to an internal view and I'd like the internal view not visible to the caller.

However, with new NavigationStack in SwiftUI 4, this seems problematic.

Scenario 1: if the caller sets up the NavigationStack without specifying path (that is, it's a non-programmtic navigation stack), we can hide the internal view by calling navigationDestination() inside the calendar library (see the approach discussed here)

Scenario 2: if the caller sets up the NavigationStack with a heterogeneous path (that is, using NavigationPath), we can do the same as scenario 1. So it's OK too.

However, using NavigationPath has limitation. It's impossible to iterate the values in the path to filter out invalid ones. For example, in a financial planning app, when user deletes an account, we'd like to remove that account's detail view in the navigation stack. In this case, we can't use NavigationPath because we can't iterate the values in the path. That leads us to scenario 3.

Scenario 3: if the caller sets up the NavigationStack with a homogeneous path (for example, an array), then we have a problem because we need to expose the internal view to the caller. The caller will need to define a value for this internal view so that it can be added to the path. The caller will also need to add the support the internal view in navigationDestination()

Since we (the library author) have no idea how caller will use the library, we need to support the worst case (the scenario 3). That means we have to expose the internal view to the caller.

Is my above understanding correct? It seems quite a limitation of the new NavigationStack API to me. How would you do it?

rayx
  • 1,329
  • 10
  • 23
  • 1
    I even read this twice... it looks as artificial problem, or I missed something. I don't see why you need to show internal views showing path - they are independent; and why to show path if you don't want, then don't show it; and why do you need iteration - it is enough to known how many remove from path to jump back and it is possible... Maybe you just show your problem in code? – Asperi Jul 14 '22 at 04:35
  • @Asperi The problem is not artificial. It arose when I tried to figure out how to rewrite my app using SwiftUI 4. 1) My assumption is that `NavigationStack` can't be nested, can they? (I did experiment and it verified my assumption). If that's correct and suppose the caller code in scenario 3 is `NavigationStack(path: [Destination]) { ... }`, one of the `Destination` value should represent the internal view. That means the library author can't hide the view to the caller. 2) My app contains multiple accounts and transfers between the accounts. – rayx Jul 14 '22 at 09:18
  • ... The account view contains all transfers it has. The transfer view contains the two accounts it's associated with. When user in an account view, he can click on one transfer to go to its transfer view. And when he is in that transfer view, he can clicks on one of the accounts to go to account view. The process can repeat forever. So an account (or an transfer) may appear multiple times in the navigation stack. That's why when user removes an account (or a transfer), I need to go through the navigation stack to remove all values. – rayx Jul 14 '22 at 09:21
  • The setup to demonstrate the issue is a bit complex. I'll see if I can work out a simple demo and let you know. – rayx Jul 14 '22 at 09:23
  • @Asperi I wrote some code but gave up, because I realized even if I finish the code it may still require a lot of explanation. I think it's easier to describe the issue in words. So I made another try. See the update section in my question above. It's long. Feel free to ignore it if you're not interested. – rayx Jul 14 '22 at 14:10
  • 1
    I find the solution! Please see the update2 section in my question. – rayx Jul 15 '22 at 00:43

1 Answers1

2

I believe the solution is to not use the value based NavigationLink API in the library code. Use the old SwiftUI 3 style NavigationLink API instead (it's not obsoleted in SwiftUI 4). The key point: it's OK to mix the two styles of NavigationLink API in a NavigationStack. And that's the only solution.

I believe this is how Apple implemented the navigation behavior for a Picker in a Form and I think Apple really should mention this approach explicitly in the doc.

More notes:

  • The fact that one can mix the two styles of NavigationLink API suggests that SwiftUI maintains a more complex navigation stack state under the hood. For example, when a view is pushed to the stack via the SwiftUI 3 style NavigationLink API, its existence can't be reflected by the content in the homogeneous path.

  • If the library is complex and has to use the value based NavigationLink API itself, I believe it has to expose those values and the corresponding navigationDestination handler to its caller. There is no way to hide these details from the caller in SwiftUI 4.

rayx
  • 1,329
  • 10
  • 23