This article provides developers information for setting up deep links to be handled within the app.
What are Deep Links?
Deep linking allows a brand to direct their customers to specific pages within the app from a Push Notification, In-app message or an Email. Thus, using deep links, it is simple for customers to see a previously abandoned cart, product pages or their wishlist amongst other things, rather than just taking the customer to the home screen. Supporting deep links, improves the customer experience by creating frictionless, personal journeys, which in turn are likely to affect conversion, and revenue positively.
URI, Universal Links and App Links
A common use for deep links is the URI schemes format, such as lsl://product
. The advantage on URI schemes is that they are easy to configure. However, if the user does not have the app installed on their device, the browser will open on an empty page. Moreover, if the scheme was not properly set in the application code, the user will land on the default application screen.
Apple introduced Universal Links on iOS 9 as a solution for the URI scheme. Universal Links are web links (e.g. https://bestrun.com) that direct to a web page or a content in the app. When a Universal link is opened, iOS verifies if there are any apps installed on the devices registered with that domain. If so, the app is opened immediately. If not, the web URL is loaded in Safari or in the default browser. Universal links are a powerful tool for marketers to make sure, that links redirect to the relevant app screen, if the app is installed on the device, or to the relevant website page, if the app is unavailable.
The equivalent to Universal Link for Android is App Links. It works in a similar way, directing the user to the app or website.
Associate App and Website
iOS
Configure the project
- Add Associated Domains capability to the project.
- In the domain section, add an entry for each domain that the app supports, prefixed with
applinks:
, such asapplinks:www.mywebsite.com
.

Configure the website
- Create an apple-app-site-association file with appID and the paths the app supports (e.g. bestrun.com/products). The format matches that of a JSON file, but it cannot have the .json extension. The appID consists of the team ID (can be found at Apple Developer Account in the membership section) combined with the app’s bundle ID (can be found in the project). Specify the paths the app will support.
{
"applinks": {
"apps": [],
"details": [{
"appID": "ABCD1234.com.ems.bestrun",
"paths": ["/products"]
}
]
}
}
- Host your apple-app-site-association file on your domain. It should be uploaded to your HTTPS web server. You can place the file at the root of your server or in the
/.well-known
subdirectory. Ex:
Android
Configure the project
The following approach will disable the disambiguation dialog (choose app to open the link):
- Set
android:autoVerify="true"
in any one of the web URL intent filters in the app manifest that include theandroid.intent.action.VIEW
intent action andandroid.intent.category.BROWSABLE
intent category.
<activity ...>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="www.bestrun.com" />
<data android:scheme="https" />
</intent-filter>
</activity>
Configure the website
- Create the assetlinks.json file with package_name (The application ID declared in the app's build.gradle file) and sha256_cert_fingerprints (The SHA256 fingerprints of the app’s signing certificate).
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.ems.bestrun",
"sha256_cert_fingerprints":
["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
}]
- Host the assetlinks.json file on the domain. This file must be published on the website to indicate the Android apps that are associated with the website and verify the app's URL intents, for example:
https://bestrun.com/.well-known/apple-app-site-association
If you would like to use multiple sites and App links from different sites or subdomains of your main site, use the following approach:
Multiple Subdomains:
If your domain has multiple subdomains (e.g. pay.bestrun.com, crm.bestrun.com), place the assetlinks.json file in the respected path of your main domain, for example, https://bestrun.com/.well-known/assetlinks.json
.
The manifest file will contain a wildcard in the android:host="*.bestrun.com"
declaration.
<activity ...>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="*.bestrun.com" />
<data android:scheme="https" />
</intent-filter>
</activity>
Multiple websites:
If you would like the second website to be added into the application, you will have to specify it in the manifest file and add another assetlinks.json file to the second website.
<activity android:name=”SecondActivity”>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" android:host="www.example.net" />
</intent-filter>
</activity>
For example: https://www.example.net/.well-known/assetlinks.json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.ems.bestrun",
"sha256_cert_fingerprints":
["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
}]
Handle Emarsys (Push and In-app) deep links
handleEvent
method will receive the deep link event name (DeepLink) and the payload (“url”: “example://deep_link”) that are sent in the push notification and in app messages.
trackDeepLink
method should not be called here.
iOS
Rich Push Message implementation is required. If not implemented, follow step 9 in our documentation.
Objective-C
- (void)handleEvent:(NSString *)eventName
payload:(nullable NSDictionary<NSString *, NSObject *> *)payload {
if ([eventName isEqual: @"DeepLink"]) {
NSObject *urlObject = payload[@"url"];
if ([urlObject isKindOfClass:[NSString class]]) {
NSURL *url = [NSURL URLWithString:(NSString *) urlObject];
[self handleDeepLink:url];
}
}
}
Swift
func handleEvent(_ eventName: String, payload: [String : NSObject]?) {
if eventName == "DeepLink" {
guard let urlString = payload?["url"] as? String,
let url = URL(string: urlString) else {
return
}
handleDeeplink(url: url)
}
}
Android
- Declare that you are implementing the EventHandler interface
- Tell the Emarsys SDK at config time which function is handling the actions from push notification and in app messages
- Create the handler function along the lines shown below.
Java
public class ApplicationClass extends Application implements EventHandler {
@Override
public void onCreate() {
super.onCreate();
...
Emarsys.setup(config);
Emarsys.getInApp().setEventHandler(this);
Emarsys.getPush().setNotificationEventHandler(this);
}
@Override
public void handleEvent(@NotNull Context context, @NotNull String s, @Nullable JSONObject jsonObject) {
if (s == "DeepLink") {
try {
URL url = new URL(jsonObject.getString("url"));
handleDeeplink(url);
} catch (Exception e) {
e.printStackTrace();
}
}
}
...
}
Kotlin
open class SampleApplication : Application(), EventHandler {
override fun onCreate() {
super.onCreate()
...
Emarsys.setup(config)
Emarsys.inApp.setEventHandler(this)
Emarsys.push.setNotificationEventHandler(this)
}
override fun handleEvent(context: Context, eventName: String, payload: JSONObject?) {
if (eventName == "DeepLink") {
val url = URL(payload?.getString("url"))
handleDeeplink(url)
}
}
}
handleDeeplink method example
Extract the URL components and redirect the user to a specific content based on the path.
iOS
Swift
func handleDeeplink(url: URL) {
let rootViewController = window?.rootViewController as! ViewController
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return
}
if components.path == "/product" {
DispatchQueue.main.async {
rootViewController.performSegue(withIdentifier: "productSegue", sender: nil)
}
}
}
Android
Kotlin
private fun handleDeeplink(url: URL) {
if (url.path == "/product") {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
}
Process deep links in the app
iOS
To track deep links with the iOS Emarsys SDK, you need to call Emarsys.trackDeepLink
in the AppDelegate's application:continueUserActivity:restorationHandler:
method.
Objective-C
- (BOOL) application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id <UIUserActivityRestoring>> *__nullable restorableObjects))restorationHandler {
return [Emarsys trackDeepLinkWithUserActivity:userActivity
sourceHandler:^(NSString *urlString) {
NSURL *url = [NSURL URLWithString:(NSString *) urlString];
}];
}
Swift
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
return Emarsys.trackDeepLink(with: userActivity) { (url) in
}
}
The (BOOL) return value of Emarsys.trackDeepLink
indicates whether the UserActivity
contained a Mobile Engage email deep link and whether it was handled by the SDK.
The first parameter is the UserActivity
that comes from the AppDelegate’s application:continueUserActivity:restorationHandler:
method.
The second parameter is optional, it is a closure/block that provides the source URL that was extracted from the UserActivity.
Android
Android Emarsys SDK automatically handles deep link tracking with most use cases, with only one exception: manual tracking is needed when your Activity has overridden onNewIntent
. In that case, you can track the deep link in the following way:
Java
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Emarsys.trackDeepLink(this, intent);
}
Kotlin
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent != null) {
Emarsys.trackDeepLink(this, intent)
}
}