VentureDevs blog

VENTUREDEVS BLOG

our press

Implementing Deep Linking in React Native apps
iOS | Android | RN >0.63

This article complements the Webinar on Deep Linking that can be found here (in Polish). If you want to know more, check out the video!

What Deep Linking is?

Imagine that you have an app that clones Medium’s functionality and allows users to add and read articles. Now, users probably want to share or promote a good content, but how can you help them navigate directly to a specific article? This is where Deep Links comes into play!

So now we know that Deep Link is a simple URI that directs a user to desired content rather than just launching the home page/main screen.

The most basic form (and easiest to implement) of a Deep Link in mobile apps is a custom URI scheme. For example, you can define a scheme like vd:// for your app that launches the app, but something like vd://careers/react-native will take a user to the screen with React Native career description.

Example schemes for well-known apps:
  • fb://
  • slack://
  • twitter://
  • This is some text inside of a div block.
There are also build-in schemes like:
  • http:// | https://
  • mailto://
  • tel://
  • sms://

Your app can have any scheme and it’s not unique (like bundle ID) or verified. This means that you can have two apps installed on your phone that use the same scheme. This is also a vulnerable part of custom schemes, because it allows malware apps to try to intercept information transmitted to your application via a Deep Link. Another disadvantage is that custom schemes do not handle any fallbacks in cases where your app is not installed. It simply will not work.

This is why another approach came up (and currently is strongly recommended by Apple) which is using a traditional http:// link that opens a web browser. First of all, it is safer because it requires that you own the domain to which links are redirecting. It also handles the process if the user does not have your app installed — it just opens the website in the browser.

On iOS, these links are called Universal Links, and on Android, App Links.

Now, let’s dive straight into implementation. You can use this tutorial to implement Deep Links in your already existent app or you can run ‘npx react-native init TestDeepLinking’ to follow.

iOS Custom Scheme and Universal Links

Official docs: https://developer.apple.com/library/archive
/documentation/General/Concept
ual/AppSearch/UniversalLinks.html

If you’ll try to run your custom scheme in Safari on your device or in simulator, you’ll see something like this:

Support custom scheme

To support a custom scheme, let’s say vd:// we need to perform 2 steps.

Step I - Add custom scheme in XCode

To do this, go to your Target Info and add a new URL Type. As an ‘Identifier’ you can use your bundle ID, for ‘URL Scheme’ you can use whatever you want. We will use vd. As ‘Role’ in this case leave ‘Editor’, but if you’re going to invoke other app’s schemes from within your app (eg. mailto://), then you would change it to ‘Viewer’.

Step II - Add code in AppDelegate.m

Go to AppDelegate.m and add this openURL method that handles incoming URLs using RCTLinkingManager provided by React Native. Please note that official docs will stress that you need to link this library, but in this version of React Native this is no longer needed and you are good to go.

#import <React/RCTLinkingManager.h>

- (BOOL)application:(UIApplication *)application
  openURL:(NSURL *)url
  options:(NSDictionary<UIApplicationOpenURLOpti
onsKey,id> *)options
{
 return [RCTLinkingManager application:application openURL:url options:options];
}
More:
https://developer.apple.com/
documentation/uikit/uiapplication/1622961-openurl
TTest it

Build your app. When you make native changes you have to rebuild it to see the effect of your work. Regular refresh won’t be enough.

Now, test if the custom scheme is working properly. Go to Safari on your device or in simulator and type in ‘vd://’. This should detect that the app is installed and ask you if you want to open the app.

To test your app via terminal, type in this command:
xcrun simctl openurl booted vd://careers/react-native
Support Universal Links

Please note, for this you’ll need a Developer Account that is fully approved by Apple. Otherwise, you won’t be able to set Associated Domains Capability in XCode.

STEP I - Enable ‘Associated Domains’ in XCode

First, you need to enable “Associated Domains’ in your project settings. To do so, go to your Target’s ‘Signing and Capabilities’ and add a new capability. Use ‘applinks:’ prefix for your domain to tell XCode that this domain is used for Universal Links.

STEP II - Add code in AppDelegate.m
Like before, go to AppDelegate.m and add this code:
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity
restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
                 continueUserActivity:userActivity
                   restorationHandler:restorationHandler];
}
More:
https://developer.apple.com/
documentation/uikit/
uiapplicationdelegate/16
23072-application
STEP III - Add AASA file on your website
Official docs:
https://developer.apple.com/
documentation/safariservices/
supporting_associ
ated_domains#3381438

Last but not least, you have to make sure that AASA (Apple App Site Association) file is properly uploaded to your server and available under this link (https is important):

https://your-domain.com/
.well-known/apple-app-site-association
The simplest structure of this file looks something like this:
{
   "applinks": {
       "apps": [],
       "details": [{
           "appID": "<TeamID>.com.vd.mobile",
           "paths": ["/careers/*"]
           }
       ]
   }
}

Here, the appID is your app’s bundle ID prefixed with your team’s ID. If you log in you can find it on developer.apple.com under Membership. The second property after appID is an array of supported paths. Also, it is possible to add more than one app —for example, if you have multiple bundleIDs per environment. Just add another object to the ‘details’ array.

With this all set up, you should have your Deep Link working as expected.

Android Custom Scheme and App Links
Official docs:
https://developer.android.com/training/app-linkshttps://developer.android.com/training/app-links/deep-linkinghttps://developer.android.com/training/app-links/verify-site-associations

To implement Deep Linking in an Android app, you are going to define Intents that are responsible for starting Activities (launching screens). Intent filter is an expression that specifies the type of intents that components can receive and actions that they perform. We want to show the user a specific screen and for this you’ll need to use the ACTION_VIEW intent action.

Go to android/app/src/main/AndroidManifest.xml and add a new intent filter right below the last one in the <activity> tag.

<intent-filter android:label="VentureDevs">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.
DEFAULT" />
<category android:name="android.intent.category.
BROWSABLE" />
<data android:scheme="vd" />
</intent-filter>
There are also two categories defined. As in official docs:
  • BROWSABLE is required in order for the intent filter to be accessible from a web browser. Without it, clicking a link in a browser cannot resolve your app.
  • DEFAULT allows your app to respond to implicit intents. Without this, the activity can be started only if the intent specifies your app component name.
You want both of them. The last piece is your custom scheme.
Also, in the same file, make sure that android:launchMode is set to “singleTask”.
<activity
 android:name=".MainActivity"
 android:launchMode="singleTask">
App Links
There are two steps to enable App Links.
STEP I - Add http/https scheme and host
<intent-filter android:label="VentureDevs" 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="vd" />
<data android:scheme="http" android:host="www.venturedevs.com" />
<data android:scheme="https" />
</intent-filter>

The android:autoVerify="true" will cause a verification of hosts defined in intent filters. It means that Android will check if your website hosts the Digital Asset Links file.

You can define more domains and more intent filters. Just read official docs carefully, which were super helpful for me.

STEP II - Add assetlinks.json file

Again, just like for iOS, make sure that the assetlinks.json file is properly uploaded to your server and available under this link:

https://your-domain.com/.well-known/assetlinks.json.

The file in its most basic form should look like this:

[{
   "relation": ["delegate_permission/common.
handle_all_urls"],
    "target": {
        "namespace": "android_app",
        "package_name": "you_application_id",
        "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:8
A:04:96:B2:3F:CF:44:E5"]
   }
}]

The SHA256 fingerprints makes up your app’s signing certificate and is stored in your keystore that you use to sign your app.

Test it

To test your app via terminal, type in this command:

$ adb shell am start

-W -a android.intent.action.VIEW

-d "yourscheme://" com.example.android

You can also test Deep Links in Android Studio.

Go to Run -> Edit Configurations. Set the launch options to your scheme, for example vd://careers (assuming that you handle incoming links in React Native and navigate to proper screen).

Handle Links in React Native

Official docs

https://reactnative.dev/docs/linking
Listen for incoming links

After you implement everything on the native side, you can handle incoming links. There are two cases.

App is open

In this case you’ll use Linking.addEventListener(type, handler) where ‘type’ is the type of triggered event, so pass ‘url’ here, and ‘handler’ is a callback to which clicked link is passed and where you can handle it. For example:

Linking.addEventListener(‘url’, (event) => {
   Linking.canOpenURL(event.url).
then((supported) => {
       if (supported) {
           yourCustomMethodToHandleUrl(event.url);
       }
   });
});

Please remember to cancel any listeners you initialize on unmounting, to do this use removeEventListener(type, handler).

App is not open

Link opens it and is passed as `initialURL`, so you are going to use Linking.getInitialURL(url).

Linking.getInitialURL().then((url) => {
   if (url) {
       Linking.canOpenURL(url).then((supported) => {
           if (supported) {
               yourCustomMethodToHandleUrl(url);
           }
       });
   }
}).catch((e) => handleErrorSomehow(e));
Handle Links

As you can see I used ‘yourCustomMethodToHandleUrl(url)’ method, this is where you want to:

  • Parse and match the link. So you want to extract the path as well any parameters passed. So from ‘https://venturedevs.com/
    careers&search=react
    ’ you will receive a path ‘careers/’ and a parameter to pass for example as an object { search: react }.
  • Navigate to screen. You most probably will have a navigation implementation where you will match the path to proper navigators and pass parameters to desired screen.
Troubleshooting

If you are going to implement a custom scheme, Universal Links and App Links all together, there are a lot of steps to do, so it's not hard to make a mistake. Here is a list of things that might go wrong.

Make sure AASA/applinks.json files are done right

One of the most common mistakes.
For AASA file make sure:

  • Your app ID and team ID are correct (especially if you have multiple environments).
  • Your file is properly uploaded and available. There should be no redirects and should be served over https://. You can validate your file here: https://branch.io/resources/aasa-validator
  • You did not append ‘.json’ to the filename.
  • If you make any changes to your AASA file, rebuild your project. iOS is validating it when you first open your app (unlike Android on every launch).
For Android
Rebuild your project

Another common mistake. After you make any changes in native files, rebuild your project.

Expect different behaviors

There is a hidden truth about Deep Links, it’s not going to work the same in all cases, this mostly applies to iOS.

  • If you paste your link directly into the browser, it won’t open your application. However, on iOS, it might show you a little baner asking if you want to.
  • If you are propet if you want to open the link in the app and you will choose browser instead it might remember your choise.
  • On iOS, behavior depends heavily on where you clicked the link. For example if you tap a link in Slack it will work properly, but in Messenger, Facebook or Instagram it might be overridden and the webview will be displayed instead. https://stackoverflow.com/a/37369719
Go through the steps again

If none of the above helped, take a break. Go for a walk. Come back and check all steps one by one, again...

Summary

You should now be able to implement:

  • Custom scheme for your app.
  • Universal Links on iOS.
  • App Links on Android.

That’s a lot. I hope you find this article useful. If you do, please don’t forget to share with other developers.

Recent articles

Recent articles

Sign up for our newsletter
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
follow us
FacebookTwitterInstagramLinkedin
Sign up for our newsletter
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.
follow us
FacebookTwitterInstagramLinkedin