Swift makes it easy to allow deep links into your app. Deep links are especially helpful when wanting to direct users straight to specific content. One way I use deep links is sending emails to my task manager. When I see the task, I can click on a link and it opens up the Mail app on my Mac or iPhone directly into the app. Another use is Evernote. I can tap on a link and have the Evernote app open with the content I want to see.
You will see these links as message://link_to_some_page/id or evernote://some_note
Lets take a look at how this works.
The first step is to add a new entry to your info.plist file. If you add “URLScheme”URL Types”, an info.plist will appear in the sidebar. Right click on it and “Open As > Source Code”, and add the following:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>deeplink</string>
</array>
</dict>
</array>
</dict>
</plist>
Line 10 is where we set our URL scheme. In this example I called it deeplink, which means our deep links will follow the format: deeplink://some_page.
Adding a Route
We will handle deep links with our own router and have an enun of known, and unknown routes. Lets implement the routes as follows:
enum AppRoute: Equatable {
case home
case profile
case settings
case unknown
init(url: URL) {
switch url.host {
case "home":
self = .home
case "profile":
self = .profile
case "settings":
self = .settings
default:
self = .unknown
}
}
}
This enum has an init method that accepts a URL. When we pass it a URL it determines if the URL is a home, profile, settings, or unknown route. We will use this a little later.
Router Class
class AppRouter: ObservableObject {
@Published var route: AppRoute = .home
func handle(url: URL) {
self.route = AppRoute(url: url)
}
}
Next, we add a Router class. This conforms to ObservableObject and has one @Published property which is an AppRoute, the struct we just created earlier.
It also has a method called handle that accepts a URL and then sets self.route based on the initialised AppRoute.
@Main
import SwiftUI
@main
struct DeepLinkApp: App {
@StateObject private var router = AppRouter()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(router)
.onOpenURL { url in
router.handle(url: url)
}
}
}
}
We create our AppRouter as an @StateObject. An @StateObject lives for the duration of the app with it being declared here. If declared in a view, it would stay alive for the duration fo the view.
Line 10 injects router to the environment.
Line 11 detects when the app opens from a URL and when it does, it receives that URL, something like deeplink://profile and calls the handle method on the router. This then sets the @Published property in that class to the correct AppRoute enum value, and because its @Published, the view can observer it and action accordingly.
ContentView and Displaying the Correct Content
Our next task is to have our ContentView load up the correct view:
struct ContentView: View {
@EnvironmentObject var router: AppRouter
var body: some View {
NavigationStack {
switch router.route {
case .home:
Text("Home")
case .profile:
Text("Profile")
case .settings:
Text("Settings")
case .unknown:
Text("Unknown Route")
}
}
}
}
On line 2 we have our @EnvironmentObject which we access. This was injected in @main in the ContentView. This means that whatever happened in there when the app detected it was opened by URL, we have access to that same instance.
With that value we can use a switch statement and detect what the router’s route property is, that being .home, .profile, .settings, or .unknown. We can then display the correct view. In this test I just displayed a Text view for each.
In Closing
You don’t specifically need a route and router and can handle this anyway you want. In short, you setup the URL Scheme in info.plist. You use .onOpenURL in the Scene, and you action it.
If you were building a note taking app, your route enum might have the following cases:
.noteList
.newNote
.settings
.profile
A URL opening .newNote could immediately open the app to a new item making a great place to quickly make a note of something. It is also useful for Apple Shortcuts integration where you might have a shortcut to do something deep within your app.
Leave a Reply
You must be logged in to post a comment.