Navigating SwiftUI Complexity: Advanced Deep Linking with NavigationSplitView
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.
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.