Episode #589

Programmatic Deep Linking with NavigationPath

8 minutes
Published on February 2, 2025

This video is only available to subscribers. Get access to this video and 588 others.

In this video, we delve into advanced navigation techniques in SwiftUI using the NavigationStack and NavigationPath APIs. We start by discussing the limitations of pre-creating views for all possible destinations and how lazy navigation can optimize this process. We then shift focus to implement deep linking within the app, allowing direct navigation to specific content. We demonstrate how to bind a navigation path to an array of Hashable elements and dynamically update the navigation state. NavigationPath gives us tools to modularize our code while maintaining state-driven navigation and deep linking.

Note: I'm traveling this week and am using a portable recording set up, in case you're wondering why the audio sounds a little different. ✌🏼

Links

This episode uses Swift 6.0, Xcode 16.2.

So last time we talked about navigation link passing in a value and then specifying a navigation destination for instances of that hashable value in order to provide the destination screen.

And this is useful for lazy navigation so we don't have to sort of pre-create all the views for all the possible destinations.

This will happen on demand.

So we have that working for fruits and vegetables here.

We've got a bunch of fruits and we've got our section, a navigation link for that item, and then the navigation destination for that item.

So this is for item, this is for fruit, and then we also have vegetable.

So what I want to talk about now is if we scroll down to the bottom I want to be able to deep link directly to some something in our application.

And right now we cannot do that.

So I'm going to scroll up to our content view and here I want to change this navigation stack to take a path.

And if we take a look at the type here we're going to take a look at this and there are a few different implementations we can use for this initializer.

This is the one we were using which we just passed a view builder.

This is one of the path ones that we can use which takes a binding to a navigation path.

We'll look at that one in a minute.

And this one is a path that takes a binding to data.

So this data has to be a mutable collection and it has to be a random access collection and a range replaceable collection.

So essentially it has to be an array of hashable elements.

So we're going to use that initializer and the array of hashable elements we're going to use is going to be fruits for now.

So I'm going to say that we have some state that is going to be var fruits and that's going to be a fruit array like this and it'll start off as empty.

And let's just call this instead of fruits this is the navigation path.

So now we can pass that path over here a binding to this path.

So now once we have that I'm going to go down here and just say on change of path and I want to just print out the path changes from the old value to the new value.

So let's go ahead and run this in our preview.

And I will tap on apples and if we look at this we can see that the path automatically changed from an empty array to the one that contains the fruit and if I hit back it goes from the one that contains the fruit to empty again.

So this is a binding which is going to keep in sync with the navigation state of our application which is good.

The next thing we can do with this is go down to our preview and we can now deep link into some fruit here.

So let's say I have a mango and also a banana and let's say a durian.

And if we go over here and add a title to our fruit view so let's go to where we are creating our fruit view this is going to be dot navigation title and we'll add a text for fruits dot title.

And we'll also do navigation display mode to inline.

So now we can see that we have the title here and we also have our back button which works and so we've deep linked directly into a navigation state that has multiple view controllers or views rather multiple views inside of it so when I back out of that and back out of that and then one more time I'm back at the root.

So this is how we can deep link into our application and provide not only this is useful for for previews but also if you need to sort of support deep linking in your application say from a URL and you want to jump straight to some content you can build it up this way.

Now what we've done so far is declare an array of fruits so that's the only thing we're going to be able to navigate to and that's not really what we want here we need this to also support vegetables and items and we don't have that in here and you might consider doing something like any or any hashable here but this sort of erases the type information and so it's actually not going to work because it needs to be able to figure out which of these navigation destination modifiers to call.

So we can't build a heterogeneous data structure for a navigation path like this we have to use the navigation path endpoint and this can be initialized to just an empty instance and this navigation path will let us append values to it so if we scroll down here we can see that this actually doesn't work anymore because we can't construct it with an array so what we're going to do instead of this is I'm going to create a content view who has a navigation path and I can create a navigation path like this and then I can append values onto it so let's grab let's make it mutable let's grab a couple of fruits that we can add onto here and a vegetable and then what else do we want to add and we'll add an item let's see and then we can return the content view with this navigation path.

Okay so now we are sitting inside of a deep linked issue here so we can go back back and back if we go forward we can see that we can see our items we can view the fruit we can view the vegetables and so this is all working it did seem like our vegetable when I hit back here was not working I'm not quite sure what happened here let's also add the navigation title to the item view and also our vegetables which is here and now it's making sense so what we want to do here so what's happening is our navigation destination is only visible from the item view it's not visible from here so if we do this let's see what our build error is we don't have a fruit that needs to be item.title so now we can see that we have item three and that so what happened before is because we declared the path before it ever reached an item view the navigation destination modifier had not yet been registered and so there was no possible view for that to to render and so it rendered a little warning view so that's something to keep in mind I do think it is useful to have your navigation destinations encapsulated for encapsulated types so you don't have to have your root screen know about all the possibilities of navigation in your entire app this definitely supports a modular approach okay and with that we are able to heterogeneously navigate between items or between screens depending on the data that we have chosen and that's it for the navigation path api in the next episode we're going to take a look at navigating with sheets