Deep Linking & Push Notifications in React Native with Pusher Beams
Creating, adding, and testing Push Notifications

At Zept, we work hard to help thousands of qualified, foreign students from around the world find the universities and colleges in Canada that not only meet their needs, but are also the most likely to accept them. For our target demographic, email doesn’t cut it and SMS has proven unreliable. Push Notifications are our best bet.
We need to send students updates about schools and notify them when university recruiters reach out to them. To do that, we use Pusher Beams.
We already use Pusher for websockets, so it shouldn’t be that hard to use Beams for Push Notifications (PN), right?
It was a tough journey, but here is how we got it to work

Add Deep Linking to your Application
In order to make PN work across both platforms, we discovered that we needed to set up deep linking. There are many guides on how to properly do this; here is an abridged section.
Deep Linking?
I am assuming you understand what deep linking is, but as a quick recap: Deep linking is setting your app up to intercept real URLs from your website and handle them in the app. For example, ‘https://test.com/message/abc' should open the app to message ‘abc’.
Clear? If not, read more here:
Why Deep Linking is needed for Push Notifications
At the time of publishing, the Pusher packages cannot send the payload from a push notification through to the JavaScript portion of your code. Until that is added, we decided the easiest way to ensure the PN opens the correct part of the app was to utilize deep linking.
For Android we will be sending a link along with our PN that will be treated as a deep link click and will send the user to the correct part of the application.
Create .well-known
folder and related files
To set up deep linking, you need to create a folder on the root of your website named /.well-known/
and add /.well-known/assetlinks.json
file. For Android, like this:
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "package_name",
"sha256_cert_fingerprints":
["package_cert_fingerprint"]
}
}]
Create a /.well-known/apple-app-site-association
file for iOS, like this:
{
"applinks": {
"apps": [],
"details": [
{
"appID": "appId",
"paths": [ "/paths-you-want-to-support", "/messenger"]
}
]
}
}
Add Deep Linking to iOS
Add entitlements to XCode so iOS knows which domains to connect to your app.
Go to Capabilities > Associated Domains, and add the domains you want to connect to your app. You will need to turn it on.

Add Deep Linking to Android
You will now need to add intent filters to AndroidManifest.xml
to handle your deep links in Android.
Here is an example of how to setup your manifest file to handle /messenger/*
links:

Handle Deep Link in Your Code
In order to handle the links, I use a version of the below code in my root component to set up listeners and to handle the initial load URL sent in deep linking.
I will go over the handleDeepLink
function later. For now, you can simply console.log
the url
.
const handleDeepLink = (url: string) => console.log(url);
Testing Deep Linking
To test the links I installed the Slack app on my test devices and sent myself a link. Then, to test, I simply clicked that link on the devices to see if it would open in the correct application with the correct console.log
of the URL.
Add Pusher Beams Push Notifications to Your Application
Prerequisites
- Create a Pusher Beams account and follow their instructions for setting up Apple and Google Firebase services
Note: We tweaked the react-native-pusher-push-notifications package. This package is not supported by Pusher, and they do not have an official React Native package at the moment
For this to work, we are using a 3rd party, not-official, package to connect Pusher’s push notification packages to React Native that we have tweaked slightly to make work.
So , what are the tweaks?
In order to use Push Notifications(PN) in Android, we made a branch of Pusher’s push-notifications-android package that handles deep linking. Special thanks to Emil Karlsson, who did a lot of the heavy lifting.
Install Pusher’s Swift Package for iOS
We use Carthage to add this dependency. Simply add the below code to your /ios/Cartfile
file. Then run carthage bootstrap
to install it.
github "pusher/push-notifications-swift" ~> 1.3.0
Follow iOS and Android instructions to install package
Follow the manual instructions here :
Subscribe Users to Beams / Interests
Now that we are hooked up to Push Beams, we need to initialize Beams in our app and subscribe the user to interests
so we can send them messages.
Zept uses Redux Observables, so we have an epic that fires as soon a user logs in. It sets up their pusher subscriptions, and a separate one unsubscribes the user when they log out.
We subscribe users to a couple of public interests.
Each user also has a custom interest code that is provided by our server (ex. user-{userId}
) that allows us to send them personalized content.
Here is a sample of our util
file’s code:
So a user will be, by default, subscribed to general
and user-{user.id}
every time they log in. This section should probably be changed to meet your needs.
Test a Push Notification
Note: iOS notification only work on physical devices. They will not work on emulators.
You can either use Pusher’s Debug Console (available when logged in) or use Postman. I prefer postman. This is a sample payload:
If you app is set up correctly, iOS should handle the Push Notification payload, and Android should open with a View intent
with your website URL. Both should log a console message of https://yourdomain/messenger/abcde
.
Also, on iOS, this Push Notification should add a badge to your app’s icon that says 5.
Handle the Deep Linking
The last step is to handle the deep link. At Zept we use React-Router on our website and React-Navigation in React Native. We wanted to reuse as much code as possible and also prevent URL changes from breaking future code. So we wrote a util
function to help us connect our routes in web and React-Native and (hopefully) prevent surprises in the future.
We use constants for our routes in both web and native. For example:
// Web:
ROUTE = {
MESSENGER_CONVERSATION: (conversationId?: string) =>
conversationId
? `/messenger/${conversationId}`
: "/messenger/:conversationId"
}// Native:
APP_STACK_ROUTE: {
MESSENGER_CONVERSATION_SCREEN:
"app_stack_routes/messenger_conversation"
}
// native then has a params object with conversationId included
Now I want to be able to match/connect those to each other, like so:
const ROUTE_MATCHES: IRouteMatches = [
{
webPath: ROUTE.MESSENGER_CONVERSATION(),
rnPath: APP_STACK_ROUTES.MESSENGER_CONVERSATION_SCREEN
}
];
Finally, I wrote a function that will take my ROUTE_MATCHES
and do the conversion automagically.
Debounce the handleDeepLink
function
I used rxjs
to debounce handleDeepLink
, as I found it occasionally fired multiple times.
export const handleDeepLink = (url: string): void => {
if (!url) return;
onChangeUrl$.next(url);
};
const onChangeUrl$: Subject<string> = new Subject<string>();
const urlSubscription: Observable<string> = onChangeUrl$.pipe(debounceTime(100));
urlSubscription.subscribe(processUrl);
Process the URL
The first step is to break the URL into its basic pieces using regex; protocol
, tld
, path,
and querystring.
const REGEX_DECONSTRUCT_URL = /^(.*?):\/\/(.*?)(\/.*?)(\?(.*))?$/;const deconstructedUrl = REGEX_DECONSTRUCT_URL.exec(url);
if (!deconstructedUrl) return;
const [originalUrl, protocol, tld, path, ignore, querystring] = deconstructedUrl;
Now we can loop through the path until one matches our ROUTE_MATCHES
above.
if (protocol === "https" && tld.includes("yourdomain")) {
for (let i = 0; i < ROUTE_MATCHES.length; i++) {
if (matchPath(ROUTE_MATCHES[i], path, querystring)) {
// loop until one matches
break;
}
}
}
To match the path to the webPath
I used pathToRegexp
(the same package React-Router uses) to match the webPath
pattern to the rnPath
pattern.
Here is the final product:
Note : you can create a NavigationService
with the instructions here:
Conclusion
So now your app should be able to:
- Handle deep links from your web app properly, allowing you to send emails, SMS, and other links that will open in your native application
- Send Push Notifications to your users that will piggy back off your deep linking to send the users directly to the right part of your application.
Thanks for reading, and I hope that this helps you improve your project.