Opening App Screens
Push notifications, in-app messages and emails can carry a link that should open a specific screen of your app. How the link reaches your app depends on the channel, so this page covers each channel separately.
Push notifications and in-app messages
What it is
The link of a push notification or an in-app message button is the exact string entered in the panel. There is no redirect and no tracking wrapper: myapp://orders/42 or https://www.example.com/orders/42 reaches the device unchanged. Clicks are reported by the SDK, not by a redirect.
You can use two kinds of links:
- A custom URL scheme, for example
myapp://orders/42. Only your app understands it. - An HTTPS link on your own domain, for example
https://www.example.com/orders/42. On iOS this works as a Universal Link and on Android as an App Link, so the same link also opens your website when the app is not installed.
How it works
- The contact taps the push notification or an in-app message button.
- The SDK reports the click to Positive User. On some platforms this needs a call from your code, see Platform differences.
- The SDK passes the link to your code (a delegate, handler or callback, depending on the platform).
- Your code opens the matching screen.
Handling the link in your own code is the recommended setup on every platform. It does not depend on how the operating system routes links, and it works the same for custom schemes and HTTPS links.
Platform differences
| Platform | Push notification tap | Push click counted when | In-app message button tap |
|---|---|---|---|
| iOS | The SDK passes the full payload to your RemoteNotificationDelegate. The link is in data["link"]. The SDK does not open it. | The tapped notification is passed to handleNotificationClick(_:), or the SDK is the notification center delegate. | The SDK passes the URL to your inAppMessageClickHandler first. Return true if you handled it. Otherwise the SDK opens it with UIApplication.open(_:) when iOS reports it can open it. |
| Android | Tapping a notification opens your launcher activity, with the payload, including link, in the intent extras. A notification shown by the SDK clears the activity stack of your app when tapped. | Your launcher activity calls handleRouteFromNotification(intent). | With a click handler set, the SDK passes the URL to it and does not open it. The click is then not reported to Positive User. Without a handler, the SDK reports the click and opens the URL in Chrome Custom Tabs or with a view intent. |
| Flutter | Banner shown in your app in the foreground: the onTap(type, link) callback of buildNotification receives the link. System notification: read the link with getPushMessage(message.data)?.link in FirebaseMessaging.onMessageOpenedApp or getInitialMessage(). | Automatically for the banner shown in your app. For a system notification, after you call notificationClickedEvent. | The same onTap(type, link) callback receives the button link. |
iOS
- Write one function in your app that takes a
URLand opens the matching screen. - Call it from your
RemoteNotificationDelegatewith thelinkfrom the payload. - Call it from your
inAppMessageClickHandlerand returntrue.
final class LinkRouter: NSObject, RemoteNotificationDelegate {
@discardableResult
func open(_ url: URL) -> Bool {
// map the URL to a screen of your app, return false if you do not know it
return true
}
// Push notification tap
func didReceiveRemoteNotification(with id: String, data: [AnyHashable: Any]) {
guard !id.isEmpty, let link = data["link"] as? String,
!link.isEmpty, link != "None", // a push without a link carries "None"
let url = URL(string: link) else { return }
open(url)
}
}
let linkRouter = LinkRouter()
UserSDK.default?.notificationDelegate = linkRouter
// In-app message button tap
UserSDK.default?.inAppMessageClickHandler = { url in
linkRouter.open(url)
}
If the SDK opens a custom scheme link itself, it does so only when canOpenURL returns true, which requires the scheme in your app's LSApplicationQueriesSchemes. Handling the link in inAppMessageClickHandler avoids that requirement.
Details: Push Notifications on iOS and In-App Messages on iOS.
Android
On Android, read the payload from the intent of your launcher activity for push notifications, and pass the intent to UserCom.getInstance().handleRouteFromNotification(intent) there to count the click. For in-app messages, set an in-app click handler. With a handler set, in-app clicks are not reported to Positive User.
See Android push notifications and Android in-app messages.
Flutter
On Flutter, route the link in the onTap callback for banners and in-app messages shown in your app. For a tap on a system notification, onTap is not called: read the link with getPushMessage(message.data)?.link in FirebaseMessaging.onMessageOpenedApp and getInitialMessage(), and call notificationClickedEvent to count the click. When you pass a payload with data['from'] == 'user_com_channel' to buildNotification without a pushMessageBuilder, the SDK opens the link itself with launchUrl and does not call onTap.
See Flutter push notifications.
Links on your own domain
If you use HTTPS links on your own domain, you host the association file on that domain: apple-app-site-association for iOS and assetlinks.json for Android. Positive User is not involved in this setup.
Email
What it is
Links in emails are tracked redirect links: each link points to your workspace domain on Positive User, in the format https://<your-workspace>.user.com/emails/c/<token>/?to=<encoded target>. Opened in a browser, the link records the click and redirects to the target. The device first sees the tracking domain, not your domain, so your own Universal Links and App Links do not open the app from an email.
How it works
To open your app from email links, Positive User hosts the association files for the tracking domain, and your app declares that domain.
- Send the following to Positive User support or your Customer Success Manager:
- iOS: your Apple Team ID and the bundle ID of the app.
- Android: the package name and the SHA-256 fingerprints of your app signing certificates. If you use Play App Signing, take the fingerprint of the app signing key from Play Console.
- Positive User publishes
apple-app-site-associationandassetlinks.jsonon the tracking domain. - Add the tracking domain to your app: an
applinks:<your-workspace>.user.comentry in the Associated Domains entitlement on iOS, an intent filter withandroid:autoVerify="true"on Android. - Handle the incoming link in your app. Your app receives the tracked redirect link, not the target URL. How to get the target and count the click depends on the platform, see below.
The apple-app-site-association file covers only tracked redirect links (/emails/c/*). Unsubscribe links and all other pages on the tracking domain always open in the browser, so recipients can unsubscribe. You do not need to do anything.
iOS
From iOS SDK 1.3.0, pass every URL that opens your app to handleEmailLink. For a link from a Positive User email, the SDK counts the click and returns the target the email links to. In SwiftUI the link arrives in onOpenURL:
.onOpenURL { url in
Task {
if let target = await UserSDK.default?.handleEmailLink(url) {
router.open(target) // a screen of your app, or the browser with the target
} else {
router.open(url) // not a link from a Positive User email
}
}
}
In UIKit, pass the URL from application(_:continue:restorationHandler:) or scene(_:continue:). The same method is available with a completion handler, which runs on the main thread:
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
guard let url = userActivity.webpageURL else { return }
UserSDK.default?.handleEmailLink(url) { target in
router.open(target ?? url)
}
}
- For a link that is not from a Positive User email, the SDK returns
nilwithout a request. Handle that link as usual. - Never open the tracking link itself in the browser: that counts a second click. Open the target instead.
- Without a connection the target still comes from the
toparameter of the link. Only the click is lost, and the error goes toUserSDK.errorHandler. The SDK waits at most 3 seconds and does not retry the click. - The target is returned unchanged, with all its query parameters. One of them,
__ca__chat, lets the chat widget on your website recognize the contact when you open the target in the browser. Ignore the parameters you do not use when you route the target in your app. - It works before the first ping and after logout.
Android and Flutter
The Android and Flutter SDKs have no method for email links. Your app receives the tracked redirect link, and the target URL is in the to query parameter. When the app opens from an email link, the browser is skipped, so the click is not counted in the email campaign statistics.
Send a test email to yourself and tap a link on a device with the app installed. The app opens on the expected screen. On iOS with handleEmailLink, the click appears in the email campaign statistics.