Notifications, Deep Links
singleTop
When a user taps a notification or deep link for your app on Android, the system fires an Intent
to open your app. By default, this will initialize a new instance of your app’s main Activity
, even if your app is already running. That means that any transient UI state may be lost.
If you’d like the more iOS-like behavior of keeping your UI as-is when your app is brought to the foreground via a notification or deep link, you can use the singleTop
launch mode. Edit your Android/app/src/main/AndroidManifest.xml
as follows:
<manifest ...>
...
<application ...>
...
<activity
...
android:launchMode="singleTop">
...
</activity>
</application>
</manifest>
Read more about launch modes and other Activity
options here.
Notifications
Skip supports the core API of Apple’s UserNotifications
framework so that your iOS notification-handling code works across platforms. The setup for integrating notification functionality into your app, however, will vary depending on the push service you are using. Skip’s Firebase support includes push messaging out of the box. Follow its instructions to support push notifications on top of Firebase Cloud Messaging in your dual-platform app.
You often want to take a user to a particular part of your app when they tap on a notification. To do so, we recommend sending a deep link URL in the notification metadata. The next section discusses how to support deep links in your cross-platform app, and our FireSide sample app demonstrates push notifications, deep linking, and using them together. Here is an excerpt showing how you might send a user to a location in your app from your UNUserNotificationCenterDelegate
:
@MainActor
public func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
// Look for a custom 'deep_link' key in the notification payload, which should be a URL with our app's scheme
let content = response.notification.request.content
if let deepLink = content.userInfo["deep_link"] as? String, let url = URL(string: deepLink) {
await UIApplication.shared.open(url)
}
}
Deep Links
Deep links allow you to bring a user to a particular part of your app. Skip supports custom URL schemes and SwiftUI deep link handling.
Darwin Setup
To support deep links in your iOS build, first follow Apple’s instructions to register your custom URL scheme in Xcode.
Only pay attention to the instructions for registering your custom URL scheme. Ignore the remaining instructions about handling deep links in your code, because you’ll be using SwiftUI’s deep link processing instead.
Android Setup
Edit your Android build’s AndroidManifest.xml
to add an intent-filter
for your custom URL scheme. For example to support myurlscheme
, add the following to your AndroidManifest.xml
:
<manifest ...>
...
<application ...>
...
<activity ...>
...
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="myurlscheme" />
</intent-filter>
</activity>
</application>
</manifest>
While iOS is flexible, Android will expect deep link URLs with the general form scheme://host
or scheme://host/path
.
SwiftUI
SwiftUI uses the onOpenURL
view modifier to intercept and process deep links. Place the modifier on a view that will be rendered when the app opens, and use its action to process the given URL. This will typically involve updating your navigation bindings to take the user to a specified location in the app, as in the following sample:
enum Tab : String {
case cities, favorites, settings
}
public struct ContentView: View {
@AppStorage("tab") var tab = Tab.cities
@State var cityListPath = NavigationPath()
public var body: some View {
TabView(selection: $tab) {
NavigationStack(path: $cityListPath) {
CityListView()
}
...
.tag(Tab.cities)
NavigationStack {
FavoriteCityListView()
}
...
.tag(Tab.favorites)
SettingsView()
...
.tag(Tab.settings)
}
// travel://<tab>[/<city>], e.g. travel://cities/London or travel://favorites
.onOpenURL { url in
if let tabName = url.host(), let tab = Tab(rawValue: tabName) {
self.tab = tab // Select the encoded tab
if tab == .cities, let city = city(forName: url.lastPathComponent)) {
// iOS needs an async dispatch after switching tabs to read navigationDestinations
DispatchQueue.main.async {
// Set nav stack to root + specified city
cityListPath.removeLast(cityListPath.count)
cityListPath.append(city.id)
}
}
}
}
}
}
Testing
On iOS, the easiest way to test your deep link handling is by entering a URL with your custom scheme into Safari. You can also write a URL into a Calendar event or a Note. iOS will linkify the text so that tapping it will open your app.
Android includes an adb
command for sending intents to the running emulator or device, including deep links. Building on our SwiftUI example above, enter a command like the following in Terminal:
% adb shell am start -W -a android.intent.action.VIEW -d "travel://cities/London"