React Native Push Notifications

Zalmy Muskal
4 min readOct 9, 2020

This is how to incorporate push notifications into your react native app that is using Firebase. There are a lot of different pieces that need to come together to get this working. So I am putting it all in one place. Also, I am making the assumption that you have already added Firebase into your React Native App and that you have set up Firebase Cloud Functions.

Let’s get started!

First, we will set up iOS for push notifications. Apple calls its notification service Apple Push Notification service or APNs. I know. Very original. Firebase’s service is called Firebase Cloud Messaging or FCM. FCM integrates with APNs to deliver the notifications and since APNs only work on real devices, you won’t be able to test with an emulator for iOS.
Therefore, as a prerequisite, you must have an Apple Developer Account already set up.

Xcode

Open your project in Xcode (the /ios directory of your React Native app).
Make sure your project is selected as well as your project target (under Targets).
Go to the “Signing & Capabilities” tab.
Click on the “+ Capabilities” button.
Scroll down and double click on Push Notifications to add it to your project.
Click on the “+ Capabilities” button again.
Scroll down and double click on Background Modes to add it to your project.
Now enable the sub-modes “Background fetch” and “Remote notifications”.
You can close Xcode.

Apple Developer Account

Adding APNs key
Go to your Apple Developer Account and navigate to Certificates, Identifiers & Profiles.
On the “Keys” menu item, register a new key.
Name the key whatever you want.
Enable the APNs service.
Click continue and save.
Download the key to your computer (remember where you save it).
Copy the Key ID.
In a new tab go to your Firebase Console settings.
Go to the Cloud Messaging tab.
Select your iOS application under “iOS app configuration”.
Upload the downloaded file and add the Key ID.
Updating App Identifier
Go back to the Apple Developer Account.
Go to the “Identifiers” menu item.
Click on your App’s Identifier.
Add Push Notifications and then save.
Update Provisional Profiles
Go to the “Profiles” menu item.
Click the + button to add a new Profile.
Make two new profiles. One for iOS development and one for the App Store.

Frontend

Finally, let us set up our code to receive the notifications. First, let us install the messaging module.
yarn add @react-native-firebase/messaging
cd ios/ && pod install
Next, iOS requires that we ask permission before attempting to send notifications. If a person says no, we can’t ask them again and they would need to go into their settings to turn on notifications manually.

async function requestUserPermission() {
const authorizationStatus = await messaging().requestPermission({
sound: false,
});

if (authorizationStatus) {
console.log('Permission status:', authorizationStatus);
}
}

If this method is called on Android it will always resolve to true (you don’t need permission on Android).
The next thing we need is to save the device tokens. This is needed in order to send out a notification or message to a device. A token is automatically generated by the device and can be accessed using the Cloud Messaging module.

messaging().getToken().then(token => {
firebase.functions().httpsCallable('saveToken')({token: token})
})

I created a method in Cloud Functions to handle saving the token. We are also going to add a listener for when the device refreshes the token.

const unsubscribeToken = messaging().onTokenRefresh(token =>  {
firebase.functions().httpsCallable('saveToken')({token: token})
})
// clean up function
return function () {
unsubscribeToken()
}

Next, we are going to add a listener for new messages or notifications while in the app.

const unsubscribeMessage = messaging().onMessage(async remoteMessage => {
console.log('A new FCM message arrived!', JSON.stringify(remoteMessage));
});
// clean up function
return function () {
unsubscribeMessage()
}

So putting that all together:

import React, { useEffect } from 'react'
import { View, Text, StyleSheet, } from 'react-native'
import messaging from '@react-native-firebase/messaging';
const App = () => { useEffect(() => {
requestUserPermission()
messaging().getToken()
.then(token => {
firebase.functions().httpsCallable('saveToken')({token: token})
})
const unsubscribeToken = messaging().onTokenRefresh(token => {
firebase.functions().httpsCallable('saveToken')({token: token})
})
const unsubscribeMessage = messaging().onMessage(async remoteMessage => {
console.log('A new FCM message arrived!', JSON.stringify(remoteMessage));
})
// clean up function
return function () {
unsubscribeToken()
unsubscribeMessage()
}
}, [])
}

Finally, the last piece of the puzzle is to add a listener for when the app is in the backend or quit. You need to put this as early as possible. So the best place is the index.js file.

// index.js
import { AppRegistry } from 'react-native';
import messaging from '@react-native-firebase/messaging';
import App from './App';
// Register background handler
messaging().setBackgroundMessageHandler(async remoteMessage => {
console.log('Message handled in the background!', remoteMessage);
});
AppRegistry.registerComponent('app', () => App);

Once you finish adding that, your frontend is pretty much set up to receive notifications. Now, all we need to do is send them.

Backend

For the backend, we need one method that saves the tokens and one that sends the notifications.

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();exports.saveToken = functions.https.onCall(async (data, context) => {
if (!context.auth) {
throw new functions.https.HttpsError('unauthenticated', 'Endpoint requires authentication!');
}
let user_id = context.auth.token.user_id
let resp = await admin.firestore().collection('tokens').doc(user_id).update({
tokens: admin.firestore.FieldValue.arrayUnion(data.token)
})
if (!!resp) return {response: 'Token was saved.'}
else return {error: 'Something went wrong.'}
})

We are saving the person's tokens to a collection called tokens and the doc is the person's user id. Using admin.firestore.FieldValue.arrayUnion we are able to add to the array because it is possible that a person has more than one device.
The logic for notifications:

let tokens = await admin.firestore().collection('tokens').doc(*personsId*).get()  
if (!!tokens.exists) {
admin.messaging().sendMulticast({
tokens: tokens.data().tokens,
notification: {
title: '',
body: ''
}
})
.then(resp => {
functions.logger.log('notification:', resp)
})
.catch(error => {
functions.logger.log('notification:', error)
})
}

sendMulticast sends the same message/notification to all the tokens we have.

At this point, you should be able to send notifications. This is just to get started. I hope this helps and happy coding!

Further reading

https://rnfirebase.io/messaging/usage
https://blog.logrocket.com/how-to-create-and-send-push-notifications-in-react-native/
https://firebase.google.com/docs/cloud-messaging

--

--