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?
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.