success!!

React Native Push Notifications with Firebase Cloud Functions

Alexi Christakis
sandbox
Published in
6 min readNov 30, 2017

--

Google’s new Cloud Functions service for Firebase is a huge addition to their offerings, and I’ve been focused on utilizing it for one feature in particular: serverless push notifications. I started working on a basic social networking app a few weeks ago, and from the onset implementing notifications was a definitive goal (quite honestly it was the purpose of the project). However, I did not want to go through the trouble of setting up a server in order to listen to my Firebase data. Luckily for me, with some basic knowledge of how to use Firebase SDKs, push notifications became quick and easy.

Setting up push notifications — even with this implementation — can be daunting, especially given the amount of set-up that is required. To follow my implementation, three Firebase services must be utilized:

  1. Firebase Authentication (technically optional)
  2. Firestore or Realtime Database
  3. Firebase Cloud Messaging (FCM)
  4. Firebase Cloud Functions

Setting Up Firebase with RNF:

Unfortunately, Google’s documentation for React Native users is fairly limited, and even the most popular integration packages skimp on thorough instructions, and have inconsistent features. I opted to use React Native Firebase (RNF). Luckily, their installation procedure is straightforward and well described.

Install node module, then link the dependency.

$ npm install --save react-native-firebase$ react-native link react-native-firebase

Create a Firebase project in the console, download the GoogleService-Info.plist and move it to: ios/[YOUR APP NAME] so the SDK can connect to your project. Open the project with XCode to ensure the file has been added properly. If it doesn’t show up in the sidebar, just drag the file in.

Then, open AppDelegate.m and add:

#import <Firebase.h>

As well as:

[FIRApp configure];

at the beginning of the didFinishLaunchingWithOptions:(NSDictionary * )launchOptions method.

Set up CocoaPods.

Generate a Podfile.

Sometimes the Podfile generates with duplicate declarations, copy and paste the following into yours if errors are reported:

# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'
target '[YOUR APP NAME]' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Pods for Sesh
pod 'Firebase/Core'
pod 'Firebase/Firestore'
pod 'Firebase/Messaging'
target 'Sesh-tvOSTests' do
inherit! :search_paths
# Pods for testing
end
target 'SeshTests' do
inherit! :search_paths
# Pods for testing
end
end

Run:

$ pod install

If any issues are encountered, refer to here.

I’m going to assume you already have a working application built with a backend database to save time — don’t worry, there are plenty of guides on how to do this, check out the docs here. At the very least, for directed push notifications, your database must store unique push tokens for each user, and presumably some data to send them.

Firebase Cloud Messaging:

First, in Xcode, add the following to AppDelegate.h:

@import UserNotifications;

// change this with extra parameter
@interface AppDelegate : UIResponder <UIApplicationDelegate,UNUserNotificationCenterDelegate>

And the following to AppDelegate.m:

#import "RNFIRMessaging.h"// add before return YES;
[FIRApp configure];
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];

// add before @end
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
[RNFIRMessaging willPresentNotification:notification withCompletionHandler:completionHandler];
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler
{
[RNFIRMessaging didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
[RNFIRMessaging didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

You will also need to enable the capabilities Push Notifications, and Remote Notifications.

To set up FCM with RNF, there are two functions that you must call after user authentication. The first is to request permissions from the user, and the second is to get the device’s unique push notification token, which returns a promise with the token. This needs to be stored somewhere in your database; I opted for the user’s document under the collection “users”. See below:

FCM = firebase.messaging();
ref = firebase.firestore().collection("users");
// check to make sure the user is authenticated
firebase.auth().onAuthStateChanged(user => {
// requests permissions from the user
FCM.requestPermissions();
// gets the device's push token
FCM.getToken().then(token => {

// stores the token in the user's document
this.ref.doc(user.uid).update({ pushToken: token });
});

});

That gives the client basic FCM functionality. There’s a lot more that can be done with the SDK, and I encourage further reading so that your push notifications become more interactive. On the Firebase side, a permission key must be supplied to the console so that Google’s servers are able to connect with APNS. This key is easily generated through Apple’s developer tools. Check out Google’s guide.

Test to make sure FCM is working by sending a notification from the Firebase console. You will need to install the release scheme of your application on a real device; the simulators don’t support APNS.

even if the console reports the status as completed the message may have failed

Debugging:

  • Make sure that the bundle identifier of your application matches that of the Firebase application, and the generated key.
  • Make sure your application requested notification permissions upon first launch.
  • Check your database to see if a token was properly generated and stored.

Firebase Cloud Functions:

Once FCM is working, the final step is to write a cloud function to listen for database activity — this is the coolest part. To start, install the Firebase CLI and login.

$ npm install -g firebase-tools
$ firebase login

Then cd into your project directory and run:

$ firebase init

When it prompts you to install node modules return yes. Once the script completes, in your React Native project you should now have a new folder: functions. Open the index.js file and begin writing functions!

To start, two dependencies have to be imported:

import functions from "firebase-functions";
import admin from "firebase-admin";
// initializes your application
admin.initializeApp(functions.config().firebase);

Since this function will be running on Google’s servers, you have access to all Firebase services.

Make sure to export any function that you want Google to upload, but feel free to write auxiliary functions that aren’t exported.

exports.sendPushNotification = functions.firestore
.document("some_collection/{some_document}")
.onCreate(event => {
// gets standard JavaScript object from the new write
const writeData = event.data.data();
// access data necessary for push notification
const sender = writeData.uid;
const senderName = writeData.name;
const recipient = writeData.recipient;
// the payload is what will be delivered to the device(s)
let payload = {
notification: {
title:
body:
sound:
badge:
}
}
// either store the recepient tokens in the document write
const tokens = writeData.tokens;

// or collect them by accessing your database
var pushToken = "";
return functions
.firestore
.collection("user_data_collection/recipient")
.get()
.then(doc => {
pushToken = doc.data().token;
// sendToDevice can also accept an array of push tokens
return admin.messaging().sendToDevice(pushToken, payload);
});
});

Key things to note:

  1. Escaping tags in your database path institutes multiple listeners. For example: “posts/{userID}/statuses/{statusID}” listens to the status collections of every user, and triggers the function if documents are changed in any one of them. You can then access userID and statusID within your function as variables.
  2. Be sure to return functions you want Firebase to execute.

Once the function is complete:

$ cd /yourprojectdirectory/
$ firebase deploy

This uploads your function to Google’s servers.

if it doesn’t show up right away, try refreshing

Also, check the logs! The Cloud Functions pane provides detailed error reports:

I had issues retrieving push tokens at first

Once it uploads without errors, try performing a write operation to the node in your database that the function is listening to, either manually or by using a simulator. If the notification doesn’t come through at first, check the logs again to make sure it’s executing properly.

If a notification appears on your device congrats! Now it’s easy to expand the feature by writing more Cloud Functions, and to implement it in other projects.

I’ll try to throw together an example/starter app for this and will link it here when it’s ready. In the meantime, I’ll do my best to respond to issues below.

more guides:

--

--

Alexi Christakis
sandbox
Editor for

Yale College Senior studying Computer Science. I like to build things.