Skip to main content
Version: Next

Universal Links

Universal links let you connect to content deep inside your app. Users open your app in a specified context, allowing them to accomplish their goals efficiently.

caution

While universal links and custom URLs are both acceptable forms of deep linking, universal links are strongly recommended as a best practice.

Overview

The purpose of universal link is to link to content within your app and share data securely.

When users tap a universal link, platforms redirect the link directly to your app without routing through any browser or your website. In addition, because universal links are standard HTTP or HTTPS links, one URL works for both your website and your app. If your app is not installed, the system opens the URL in browser of your choice, allowing your website to handle it.

Use Universal links to link to content within your app and share data securely.

For the application to handle URL, the application should be launched at least once after it is installed.

There are 3 kind of linking on Android platform.

Deep links are URIs of any scheme (http, https, mailto, geo etc.) that take users directly to a specific part of your app.

Web links are deep links that use the HTTP and HTTPS schemes.

Android App Links are web links that contain the autoVerify attribute. This attribute allows your app to designate itself as the default handler of a given type of link. So when the user clicks on an Android App Link, your app opens immediately if it's installed—the disambiguation dialog doesn't appear.

<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" />
<data android:scheme="https" />

<data android:host="myownpersonaldomain.com" />
</intent-filter>

Exmaple of the Disambiguation Dialog:

danger
  • On Android 12 and higher, clicking a web link (that is not an Android App Link) always shows content in a web browser. You have use to the Android App Link for better user experience on Android 12 and above.

To verify that you own both your app and the website URLs, complete the following steps

1. Add intent filters that contain the autoVerify attribute.

Add intent filters to config/Android/AndroidManifest.xml. You have to change android:host with your own domain.

<activity android:name=".A" ... >
<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" />
<data android:scheme="https" />
<data android:host="myownpersonaldomain.com" />
</intent-filter>
</activity>

To handle any specific sub-url or apply regex, use android:pathPattern instead of android:path. For more information, refer here:

https://developer.android.com/guide/topics/manifest/data-element

danger

The path, pathPrefix and pathPattern properties must start with /

2. Declare the association between your website and your intent filters

A Digital Asset Links JSON file must be published on your website to indicate the Android apps that are associated with the website and verify the app's URL intents. The JSON file uses the following fields to identify associated apps:

  • package_name: The packageName declared in the config/project.json.
  • sha256_cert_fingerprints: The SHA256 fingerprints of your app’s signing certificate. You can use the following command to generate the fingerprint via the Java keytool:
      keytool -list -v -keystore my-release-key.keystore
    If you're using Play App Signing for your app, then the certificate fingerprint produced by running keytool locally will usually not match the one on users' devices. You can verify whether you're using Play App Signing for your app in your Play Console developer account under Release > Setup > App Integrity.

Example of assetlinks.json

[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "com.example",
"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"]
}
}]

You have to publish assetlinks.json file at the following location:

https://domain.name/.well-known/assetlinks.json

Be sure of the following:

  • The assetlinks.json file is served with content-type application/json.
  • The assetlinks.json file must be accessible over an HTTPS connection, regardless of whether your app's intent filters declare HTTPS as the data scheme.
  • The assetlinks.json file must be accessible without any redirects (no 301 or 302 redirects).

You can get more information in Android App Link Documentation.

Use Universal links to link to content within your app and share data securely.

When users install your app, iOS checks a file stored on your web server to verify that your website allows your app to open URLs on its behalf. Only you have the ability to store this file on your server, securing the association of your website and your app.

iOS will check if a Universal Link has been registered (an AASA (apple-app-site-association) file should be there in the domain which contains the bundle id of the app and the paths the app should open) for the domain associated with the link, then check if the corresponding app is installed. If the app is currently installed, it will be opened. If it’s not, Safari will open and the http(s) link will load.

You can access more about custom schemes

caution

Do not forget to change your Bundle Identifier if you haven't already.

Register your app on developer.apple.com

You can follow various documentation or simply go to Apple website and create your app registration:

https://developer.apple.com

Enable Associated Domains on your app identifier

  • Configure your website to host the apple-app-site-association file

Apple App Site Association File

This file associates your website domain with your app and must be hosted at

  • https://[yourdomain]/apple-app-site-association or
  • https://[yourdomain]/.well-known/apple-app-site-association

The AASA file contains a JSON object with a list of apps and the URL paths on the domain that should be included or excluded as Universal Links. Here is a sample AASA file:

{
"applinks": {
"apps": [],
"details": [{
"appID": "AABBCCDDEE.io.smartface.test",
"paths": [
"*"
]
}]
}
}
  • appID consists of two fields; team id and the bundle id. In this example team id is AABBCCDDEE and the bundle id is io.smartface.test
  • paths field consists of an array of strings that specify which paths are included or excluded from association
info

Multiple apps on the same domain are also supported. To do that, you’d need to add a new appID, path dictionary to the details array in the AASA file to look something like this:

{
"applinks": {
"apps": [],
"details": [
{
"appID": "AABBCCDDEE.io.smartface.test",
"paths": [ "NOT /e/*", "*", "/", “/archives/201?/* ]
},
{
"appID": "FFGGHHIIJJ.io.smartface.test2",
"paths": [ "NOT /e/*", "*", "/", “/archives/200?/* ]
}
]
}
}

Create app.entitlements File

  • Create a new file named app.entitlements under /config/iOS in your workspace
  • Here is a sample app.entitlements file:
<?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>com.apple.developer.associated-domains</key>
<array>
<string>applinks:[yourdomain]</string>
</array>
</dict>
</plist>

Handle Incoming URL and Parameters in App

In this section we will explain how your app to respond to an incoming universal link. Smartface Framework provides an event called onApplicationcallReceived where universal links are received.

Since this is an event, you can invoke this anywhere but be aware that your events will not be registered until the the code reaches there. Therefore, the best practice is to invoke this event in /scripts/app.ts file in order to be invoked right away at the beginning..

info

On iOS, you have to declare&return a value on UserActivityWithBrowsingWeb event. Then, you can use ApplicationCallReceived method freely. Otherwise the call will not trigger.

Note that this function will be executed everytime when application is launched. Make sure to handle that case, otherwise the app can behave unexpectedly since it is called directly upon launch. E.g. your route actions might not work without an URL.

function deeplinkHandler({ url: string }) {
// Do something with url. Your handler could also be here instead of the other method on iOS.
}

Application.ios.onUserActivityWithBrowsingWeb = (url) => {
deeplinkHandler({ url });
return true; // True means the application has handled this activity. Return false if you don't want to handle this action.
};

Application.on('applicationCallReceived', (e) => {
deeplinkHandler(e);
};

info

It's always a good idea to test the value returned from the parameter to get the values in the e parameter in published application. You cannot receive your specialized universal link calls in emulator.

Application.on('applicationCallReceived', (e) => {
alert(JSON.stringify(e, null, '\t'));
deeplinkHandler(e);
};

After we've written the baseline methods and handled the url, you will typically want to redirect to another page and do some actions in it.

For this example, we will write a different function and handle our routing there.

routes/deeplink.ts
/**
* We have moved deeplinkHandler to a different file to reduce clutter in one file.
*/
export async function deeplinkHandler({ url }: { url: string }) {
const parsedUrl = new URL(url);
}

You can use URL Class in Javascript to parse your URL or you can use another parser tool.

https://developer.mozilla.org/en-US/docs/Web/API/URL

Getting the Current Router Outside of the Page

You can use currentRouter method in Router:

import { NativeRouter } from "@smartface/router";

NativeRouter.currentRouter; //Holds the information of the active router info.

Combining them together, we can write a snippet like this:

routes/deeplink.ts
import { NativeRouter, NativeStackRouter } from '@smartface/router';

export async function deeplinkHandler({ url }: { url: string }) {
const parsedUrl = new URL(url);
const path = parsedUrl.pathname;
NativeRouter.currentRouter.push(path); // This is a sample code, this might not work for your case.

/* If your route is a modal and if you want to dismiss first: */

if (NativeRouter.currentRouter instanceof NativeStackRouter) {
NativeRouter.currentRouter.dismiss({
after: NativeRouter.currentRouter.push(path); // First dismiss, then push to somewhere else.
})
}
}

In order to ease out the development process, you can test redirection and handling in the emulator with the scheme of emulator. If you trigger redirection to smartface-emulator:// url on your mobile phone, it will open the Smartface Emulator.

You can pass your parameters using this scheme to test your workflow. An example: smartface-emulator://product?id=3

If you've configured your routes and actions as it's stated above, this will open up another page and show details of the product with id equal to 3.

Testing out the Scheme

You can send yourself an email to click on the link for the mobile phone to be able to recognize the url you have provided. This method is not an only way to test links. All you have to do is to let mobile phone to recognize the link.