Navigating SwiftUI Complexity: Advanced Deep Linking with NavigationSplitView

Saeid Rezaeisadrabadi
3 min readJan 22, 2024

--

In this story, I want to expand the DeepLink implementation from my previous story by introducing a slightly more complex deep link with NavigationSplitView, a feature requested in the comments.

If you haven’t read the previous post, you can find it here. I recommend taking a quick look before delving into this one.

I won’t delve into NavigationSplitView details in this post. If you’re interested, refer to the Apple documentation for more information.

Generated with AI

Implementation

Let’s add NavigationSplitView to one of our View

NavigationSplitView {
VStack {
// TextField
}
.background(.red)
} content: {
Text("Your name is ")
.foregroundStyle(.black)
.padding()
VStack {
// TextField
}
.background(.green)
} detail: {
VStack {
Text("Your name and surname is")
.foregroundStyle(.white)
.padding()
}
.background(.blue)
}

I want to have two TextField in the Sidebar and Content sections, prompting users for their name and surname. In the Detail section, I will display the entered name and surname.
Subsequently, I will populate two TextFields and theDetail section with a DeepLink. You can adapt this approach to your use case; my focus is to demonstrate how to easily handle additional deep links using the Chain of Responsibility pattern, ensuring maintainability in complex apps.

Before adding the TextFields, let’s incorporate new data handling into the DeepLinkManagerFeature2.

First, I need two variables to store the name and surname coming from the deep link.

var name: String = ""
var surname: String = ""

Next, I require a function to extract data from deep links. I won’t implement it using regex or a third-party solution. You might need a more efficient method for data extraction in your application.

private func extractData(from deepLink: String) {
let temp = deepLink.replacingOccurrences(of: "deeplink://feature2?", with: "")
let params = temp.components(separatedBy: "&")
guard params.count == 2 else { return }
name = params[0].replacingOccurrences(of: "name=", with: "")
surname = params[1].replacingOccurrences(of: "surename=", with: "")
}

The final implementation looks like this:

@Observable
class DeepLinkManagerFeature2: DeepLinkFeatureManager {
var name: String = ""
var surname: String = ""

func handle(deepLink: String, selectedTab: inout Tab) -> Bool {
if deepLink.contains("feature2") {
selectedTab = .feature2
extractData(from: deepLink)
return true
}

return false
}

private func extractData(from deepLink: String) {
let temp = deepLink.replacingOccurrences(of: "deeplink://feature2?", with: "")
let params = temp.components(separatedBy: "&")
guard params.count == 2 else { return }
name = params[0].replacingOccurrences(of: "name=", with: "")
surname = params[1].replacingOccurrences(of: "surename=", with: "")
}
}

The next step is to connect my View to this manager, so let’s create a State variable.

@State var deepLinkManager: DeepLinkManagerFeature2

and then change the view to use vars from DeepLinkManagerFeature2 for TextFields.

NavigationSplitView {
VStack {
TextField(
"",
text: .init(
get: { deepLinkManager.name },
set: { deepLinkManager.name = $0 }
),
prompt: Text("Type your name").foregroundStyle(.white)
)
.padding()
.foregroundStyle(.white)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.red)
} content: {
Text("Your name is \(deepLinkManager.name)")
.foregroundStyle(.black)
.padding()
VStack {
TextField(
"",
text: .init(
get: { deepLinkManager.surname },
set: { deepLinkManager.surname = $0 }
),
prompt: Text("Type your surname").foregroundStyle(.white)
)
.padding()
.foregroundStyle(.white)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.green)
} detail: {
VStack {
Text("Your name and surname is \(deepLinkManager.name) \(deepLinkManager.surname)")
.foregroundStyle(.white)
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.blue)
}

In the final step, I need to inject DeepLinkManagerFeature2 from ContentView/first view.

Feature2(deepLinkManager: deepLinkManager.features[1] as? DeepLinkManagerFeature2)
.tabItem {
Text("Feature 2")
}
.tag(Tab.feature2)

Keep in mind, that in a production app, you’d need a more robust solution than [1] as? ... :)

Everything is set; open this deep link from Safari, and you’ll observe the app opening, switching to the second Tab, and pre-filling the TextFields:
deeplink://feature2?name=Saeid&surename=Rezaei

Conclusion

In conclusion, this post showcased the expansion of DeepLink implementation with NavigationSplitView, responding to a reader’s request.
In essence, this post serves as a concise guide for developers aiming to navigate complexity in SwiftUI applications through efficient deep link management with the Chain of Responsibility pattern.
You can find the source code here.

In the next story, I will try to implement the same features with Composable Architecture.

--

--

Saeid Rezaeisadrabadi
Saeid Rezaeisadrabadi

Written by Saeid Rezaeisadrabadi

Over 8 years of experience in iOS software development

No responses yet