Deep Linking Push Notifications with React Navigation

TAP DAT NOTIFICATION, FIND DAT SCREEN!

Deep Linking Push Notifications with React Navigation

This post was originally published on Medium by me on Dec 6, 2020

When a user taps on a push notification, they expect to land on the screen the notification is about. When I started to implement this feature, I found no easy way to do it. Although, I had deep linking figured out, still there was no definitive approach to pass a deep link in push notification and let the link do the magic of taking the user to the intended screen.

We know where to go, but not how to

React Native Firebase project suggests a simple case of setting initialRouteName in the Navigator tag after extracting the route name from the notification’s data object. Refer: https://rnfirebase.io/messaging/notifications#handling-interaction

☝️ Firebase Cloud Messaging suggest this mechanism to handle Push Notification for redirection

This has some obvious drawbacks:

  1. It seems to be convoluted to handle a situation where screens are scattered in nested Navigator tags across various files.
  2. It is not obvious how to deal with parameters that may be required to pass to the screen to render it
  3. We have a perfectly working deep linking mechanism to open the app and auto-navigate with parameters, why can’t we reuse it instead of reinventing the wheel?
  4. If you try use a solution that lets you redirect to any screen from anywhere without props (https://reactnavigation.org/docs/navigating-without-navigation-prop/), you will end up requiring to wait for the navigator to render to have this working. (this was my first solution, but I always ended up triggering the redirection/navigation as soon as the app starts before the Navigator is rendered, causing crash)

Lucky for us, not long ago, a PR (https://github.com/react-navigation/react-navigation/pull/8987) sorted this out for us. This article is about how to implement it.

I assume you have push notification functional. If not, head here to get that working first: https://rnfirebase.io/messaging/usage. I also assume that you have React Navigation configured, and you are on a version 5.8.10 or above. I am using "@react-navigation/native": "^5.8.10" in this article.


Before the linking is configured our app looks something like this:

☝️ Simplest App with React Navigation

How to configure deep linking

Deep linking using React Navigation is well explained here: https://reactnavigation.org/docs/deep-linking However, it took me some time to figure out how to support deep links that have myapp:// as well as https://app.myapp.com/ on Android phones. Trick is to add two different intent filters one for each like this:

☝️ Configure URL Scheme in Android in a way that handles custom as well as HTTPS prefixed URLs

Now we gotta tell the NavigationContainer that we are reacting to these schemes, so my App.tsx becomes:

☝️ Deep Linking Done!

At this point, you rebuild your app (commonly, using npx react-native run-android), and you should be able to do the following from your CLI:

☝️ Test if your deep links work

Now that the deep linking is sorted, we can now freely share the deep link with https scheme in emails and websites and when people click this deep link on their desktops or phones that does not have your app installed, it will take them to your website where they can see the web version or you ask them to install the app nicely. If they have the app, they would find it delightful.

Push Notification payload contains a data attribute. You can send any key-value pair as data. If we just pass the deep link as a part of data object and somehow we tell our navigation mechanism to open the link as the first screen instead of the home screen of the app, our work is done.

React Navigation provides a neat little function in the linking attribute of the NavigationContainer tag. NavigationContainer commonly contains all the application (and definitely all the routes). This function is named getInitialUrl and it is called every time the application launches from the quit state. It is where you will land when the app is opened from a push notification. So, we need to put the logic here to redirect to the appropriate screen. Since we cleverly pass the deep link as a part of push notification, we can let React Navigation deal with it.

Here is how our linking attribute looks like once we add getInitialUrl function to it (refer: https://reactnavigation.org/docs/navigation-container#linkinggetinitialurl):

☝️ Handle the case when Push Notification arrives and the app is closed

But what about the case when we receive the push notification when our app is in the background? You need to add a listener in that case. It looks like this (refer: https://reactnavigation.org/docs/navigation-container/#linkingsubscribe):

☝️ Handle the case when Push Notification arrives and the app is running in background

We are done now. When a push notification is tapped either getInitialUrl is called or onNotificationOpenedApp invokes our listener passed in subscribe method. In both cases, React Navigation handles the incoming URL and redirects to the right screen.

To test this, go to Firebase console, to the project that’s linked to this app (URL looks something like this: console.firebase.google.com/u/0/projectYOUR-PROJECT-NAME-HERE/notification/compose), fill out fields. In the Custom Data field, add a link key, with value to one of the valid deep links.

At the bottom of the screen there is a way to push key-value payload

☝️ At the bottom of the screen there is a way to push key-value payload

Assuming you have your device’s FCM token handy, send a test notification.

Keep your FCM token handy to test your code

☝️ Keep your FCM token handy to test your code

Recline your chair, raise your arms, take a sip of hot tea while you wait for the notification to appear (should not take more than a moment), tap the notification and watch yourself chauffeured to the screen mentioned in the link. You are done for the day.