Listening to application urls in React Native

Note: there is an example application available on GitHub

One great feature of mobile platforms is deep linking into your application from custom URLs, using custom URL schemes. We wanted this for the React Native Playground iOS app, to share applications easily from any medium, like emails or tweets, using the rnplay:// scheme.

Fortunately, React Native bundles a library to handle incoming requests via URL schemes. Let’s get to it!

  • Define our URL scheme in XCode
  • Modify AppDelegate.m to pass incoming requests to React Native
  • Listen for and handle requests in JavaScript

Defining a custom URL scheme

Custom url schemes are defined in your Info.plist file. Not too long ago you would have had to manually add keys there. Luckily you don’t have to anymore! Instead you can add URL types comfortably through the XCode interface.

Select your project in the left sidebar. Then select the target you want to add URL types to. Switch to the “Info” panel and open the “URL Types” section. Copy your bundle identifier into the “Identifier” and your desired URL scheme into the “URL Schemes” field. You can ignore the other fields (we do not need an icon and “roles” are only used on OSX). You should now have something similar to this:

url types editor

Now your application is ready to respond to custom urls! (In the example above it would respond to any urls starting with myapplication://.

Modifying your application delegate file

This is only necessary to handle URLs passed to a launched application. Since apps often run in the background, we should do this. In your Appdelegate.m add this to the top:

Then add this to the end of the file:

This will make sure, that whenever somebody visits a link – with an URL scheme your application registered for – you will be properly notified about it and can take action. Even if your application is already launched, hooray!

Depending on your project file header path configuration it is possible, that XCode will complain that it cannot find RCTLinkingManager.h. But fear not! There is an easy solution to the problem. In XCode, select your project in the left sidebar. In the main panel navigate to “Build Settings”. Now scroll down until you find “Header Search Paths”, or alternatively use the search box at the top right. When you found them, double click on the value and using the “+” button at the bottom add a new path with the value $(SRCROOT)/node_modules/react-native/Libraries and set it to “recursive”. That should stop XCode from crying about not finding the headers. This is how it should look:

header search paths config

No more changes to your Appdelegate.m are required. Your file should look similar to this now:

Hooking up the JS side of things

Check out the final code, then let’s walk through it step by step.

I’m using ES6 class syntax here – you’re welcome to use React.createClass if you prefer.

We require the LinkingIOS module first.

We want to handle incoming requests as soon as our component is mounted, so we add a listener for the url event.

To keep things tidy, we remove the handler again when the component gets unmounted, even though it’s unlikely that will happen to our root component.

The real meat is in _processURL. We grab the url prop of the event, strip our custom url scheme, and finally separate the path from the query string.

Using the qs module (from npm), we parse the query string, and log the path and params to the console. Now we have everything we need to process our custom urls.

Testing our URL scheme

Let’s try launching this application, then backgrounding it by pressing the Home button.

Open Safari and visit myapplication://some-path?param=value. We’re sent back to our app. In the Xcode console, we should see some-path and {param: ‘value’}. Yay!

But what if our application hasn’t been launched yet? Let’s try it by exiting our app completely. To do this, double tap the Home button and swipe-up on our app.

Now repeat the Safari step.

Our app launches, but just sits there displaying the home screen – no log in our console.

This happens because no url event is triggered on application start. Let’s update our code to handle this case.

Take a look at the componentDidMount method on line 16. If the application has been launched via a URL, we can retrieve it using LinkingIOS.popInitialURL() and pass the URL to our handler. Since our event handler expends an event object with a url property on it, we wrap the url accordingly. In case you are wondering: {url} is the the ES6 object short notation and is equivalent to writing {url: url}.

Now you should be ready to use all the crazy url schemes you want. Try it!

Thanks to @jsierles for proof-reading.

Leave a Reply

*