Thoughts on NavigationDestination
We were all excited when iOS 16 introduced NavigationStack
and NavigationDestination
. These (not so) new tools provide options for implementing data-driven navigation. We can use these new APIs to further drive the UI using our state, calling it state-driven.
I would like to focus on a specific part of navigationDestination
and dive a bit into how we can extend it to simplify its use.
Let’s assume we have a collection of items we want to display in a list and tapping on an element takes us to a detail presentation of that item.
We can solve this using navigationDestination in two ways, but one of them requires our data type to conform to Hashable
. Let’s go with the less constrictive option and use the Bool
binding to show or hide our destination.
There are two interesting things that stand out. First, we need two variables for displaying the detail view - a Bool
and the actual item. Additionally, the view inside the navigationDestination
is constructed immediately when the parent body is evaluated, restricting us from initializing the content lazily.
Can we change this in some way?
The eager evaluation is actually simple to solve. We just slap an if let statement in the closure and are done with it.
In order to reduce the number of required variables, a simple way would be to instantiate a binding directly in the .navigationDestination(isPresented: destination:)
method. We simply fill out the missing logic and we’re good to go. This compiles and works just fine.
Now a question presents itself. Can this be done in a way that abstracts this binding creation away from the callsite. And there is a way - a ViewModifier. We put the logic inside a new modifier and add an extension to View. And there we have it, a new call that evaluates a presence of value and passes it as an argument to the destination closure.
This is the end of this simple exploration of modern SwiftUI navigation. The example shows how you can easily modify your navigation destinations to act upon an optional value being present instead of using a dedicated Bool to control the display. Here is an example of how to use the end result of this excercise.
Another example of this approach can be seen in my Github browser app, Oktatrack.
If you’ve come this far - this is not the end. It’s just the first step into modern navigation. Imagine you could build your navigation tree with mutually exclusive destinations. Think of an enum for defining what the current navigation path is. This is explored and explained in detail with PointFree’s modern SwiftUI navigation series from which this exploration also got the inspiration. Check it out and enjoy the benefits of clearly defined navigation structure in your apps.