Deep Linking with React Native
· 5 minute readRecently I added deep linking integration to one of my projects and I want to share my experience with you. It is a complete guide which covers all the steps and explains how to add deep links both on iOS and Android. In my example, I’ll use React Native Router Flux. It is a very nice navigation library and at the moment of writing, I couldn’t find anything which I would like more. In case you are using something else, this tutorial still will be useful for you as it doesn’t rely on the navigation library but describes how to integrate deep links to your application.
What is deep linking?
Deep links allow us to link to specific screens in a mobile application rather than simply launching the app. fb://profile/4 is an example of a deep link. If you have Facebook installed on your phone try opening that link. It will launch the Facebook application and show you Mark Zuckerberg’s profile.
Example application
I prepared a example application which has two screens. On the first one we are offered to enter our name. When we submit it we are taken to the second screen where we can see a greeting message. Our goal will be to allow deep links with parametrized names to the greeting screen. You can check the sources here.
Deep linking with React Native
In order to process incoming links in our React application, we need to use Linking API from React Native. According to the official docs, to handle initial URL which was used to open our application we can use the following code:
Linking
.getInitialURL()
.then(url => handleOpenURL({ url }))
.catch(console.error);
To handle incoming URLs when the app is running in background we have to attach a listener which will be called when such event occurs:
Linking.addEventListener('url', handleOpenURL);
handleOpenURL
has to navigate us to the corresponding scene based on URL which it will receive with the event. I’ll use crossroads to parse the route. This routing library does too much for such simple application, but it might become very useful when your application gets bigger:
let scheme = 'exampleapp';
handleOpenURL(event) {
if (event.url && event.url.indexOf(scheme + '://') === 0) {
crossroads.parse(event.url.slice(scheme.length + 3));
}
}
We can put this code anywhere we want. For our example project let’s create a new LinkedRouter component and listen for incoming links at componentDidMount
:
import React from 'react';
import { Linking } from 'react-native';
import { Router } from 'react-native-router-flux';
class LinkedRouter extends React.Component {
constructor(props) {
super(props);
this.handleOpenURL = this.handleOpenURL.bind(this);
}
componentDidMount() {
Linking
.getInitialURL()
.then(url => this.handleOpenURL({ url }))
.catch(console.error);
Linking.addEventListener('url', this.handleOpenURL);
}
componentWillUnmount() {
Linking.removeEventListener('url', this.handleOpenURL);
}
handleOpenURL(event) {
if (event.url && event.url.indexOf(this.props.scheme + '://') === 0) {
crossroads.parse(event.url.slice(this.props.scheme.length + 3));
}
}
render() {
return <Router { ...this.props }/>;
}
}
LinkedRouter.propTypes = {
scheme: React.PropTypes.string.isRequired
};
The only thing which is left is mapping incoming URLs to the corresponding scenes. Nice place to add this code is our router configuration. Here we assign Greeting
screen to the greetings/{name}
route and pass the name
parameter as the prop:
import React from 'react';
import { Scene, Actions } from 'react-native-router-flux';
import crossroads from 'crossroads';
import LinkedRouter from './components/LinkedRouter';
import HomeScreen from './components/HomeScreen';
import GreetingScreen from './components/GreetingScreen';
const scenes = Actions.create(
<Scene key="root">
<Scene key="home" title="Home" component={HomeScreen} initial={true}/>
<Scene key="greeting" title="Greeting" component={GreetingScreen}/>
</Scene>
);
// Mapping incoming URLs to scenes
crossroads.addRoute('greetings/{name}', name => Actions.greeting({ name }));
export default <LinkedRouter scenes={scenes} scheme="exampleapp"/>;
At this point, our React application is ready to handle incoming links. There is a little configuration work left to get it working on iOS and Android.
iOS deep linking
On iOS, we’ll need to link RCTLinking
library which comes with React Native to our project. For this we’ll have to do the following steps:
- Open project
*.xcodeproj
with XCode. - Drag
RCTLinking.xcodeproj
fromnode_modules/react-native/Libraries/LinkingIOS
to the projectLibraries
- Click on your main project file (the one that represents the .xcodeproj) select
Build Phases
and drag the static library from theRTCLinking
Products
folder toLink Binary With Libraries
- Click on your main project file again, select
Build Settings
, search forHeader Search Paths
and put$(SRCROOT)/../node_modules/react-native/Libraries
there - Click on your main project file one more time, select
Info
and add a URL type at the bottom. We’ll putexampleapp
there.
If you want to listen to incoming app links during your app’s execution you’ll need to add the following lines to the *AppDelegate.m file:
#import "React/RCTLinkingManager.h"
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [RCTLinkingManager application:application openURL:url
sourceApplication:sourceApplication annotation:annotation];
}
// Only if your app is using [Universal Links](https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/AppSearch/UniversalLinks.html).
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler
{
return [RCTLinkingManager application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
}
Now you can open Safari and navigate to exampleapp://greetings/World
. You’ll be taken to the Greeting screen:
Android deep linking
In order to allow deep linking to the content in Android, we need to add intent filters to respond to action requests from other applications. Intent filters are specified in your android manifest located in your React Native project at android/app/src/main/AndroidManifest.xml
. Here is the modified manifest with the intent filter added to the main activity:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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="exampleapp"
android:host="greetings"
android:pathPrefix="/" />
</intent-filter>
</activity>
After updating the manifest file you can launch the application on Android Virtual Device and execute the following command in a terminal to test the deep link:
adb shell am start -a android.intent.action.VIEW -d "exampleapp://greetings/World" com.reactnativedeeplinkingexample
The same as with iOS application you should see the “Hello World” message.
Summary
That’s pretty much it. As with many other things with React/React Native it was very easy to add deep links to our example app. I used React Native Router Flux navigation library but as you saw it was all about implementing the handleOpenURL
function and it shouldn’t be a problem to add deep linking to your application following steps from this tutorial.