Skip to main content

React Native: Integrating Push Notifications using FCM










Recently, I integrated push notifications in my react native app using react-native-firebase. I came across many challenges during the process of integration to successful implementation, so I thought to share steps with everyone here.

Create Application On Console

First create an application on Firebase console. Follow steps in Cloud Messaging Section, add iOS and Android apps as per your requirement.

Cloud Messaging Section — Firebase

Make sure your google-services.json and GoogleService-Info.plist are placed in correct folders.

Correct Placement of Google Services files

Android
Configure gradle files. Please use the latest firebase dependencies available while following this tutorial. You can find official FCM guide here. For your reference, my gradle files are as follows:

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.facebook.react:react-native:+'
//Add these lines
implementation "com.google.android.gms:play-services-base:15.0.0"
implementation "com.google.firebase:firebase-core:15.0.2"
implementation "com.google.firebase:firebase-messaging:15.0.2"
}
//Put this on bottom of file
apply plugin: 'com.google.gms.google-services'

App module build gradle snippet

buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.google.gms:google-services:3.2.1'
}
}

Root build gradle snippet

iOS
  1. In Firebase console, you have to include either APNs Authentication Keyor APNs Certificate in Project Settings > Cloud Messaging in order to receive push notifications. Creation of push certificate is out of scope in this tutorial, so here is a nice link for your reference :)
Note: You need two separate push certificates. One for Sandbox Environment (Development Certificate) and other for Production (Distribution Certificate). Both should be uploaded to Firebase Console.
2. Turn on following two capabilities in Xcode. Make sure these are turned on in certificates as well created in step 1.
a) Push Notifications
b) Background Modes - Check only Remote Notifications


Make sure you open <YourProject>.xcworkspace file using Xcode instead of <YourProject>.xcodeproj.
3. Open Podfile and edit its contents as follows:

# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'
target 'ReactPushNotifications' do
# Uncomment the next line if you're using Swift or would like to use dynamic frameworks
# use_frameworks!
# Pods for ReactPushNotifications - Add these lines
pod 'Firebase/Core'
pod 'Firebase/Messaging'
target 'ReactPushNotificationsTests' do
inherit! :search_paths
# Pods for testing
end
end

Podfile contents snippet

If you haven’t setup pods before, please follow guide from here. If you face any error, you can check official FCM guide.

Install Firebase Module

Run following command in root folder of your react project.
npm install --save react-native-firebase
You can link firebase node module with native applications using following command:
react-native link react-native-firebase
Here I will be linking firebase node module with native platforms manually without react-native link, since it avoids mess and incomplete bindings most of the times. Moreover, if you are face any issues after linking, you can confirm steps from below that everything is included correctly in native platforms.
Android Setup
  1. Edit MainApplication.java:
import io.invertase.firebase.RNFirebasePackage;
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;                       import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;
@Override
protected List<ReactPackage> getPackages() {
  return Arrays.<ReactPackage>asList(
 new MainReactPackage(),
 new RNFirebasePackage(),
 new RNFirebaseMessagingPackage(),
 new RNFirebaseNotificationsPackage()
 );                               
}
2. Add these lines in settings.gradle
include ':react-native-firebase'                       project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android')
3. In app build gradle, add dependency:
dependencies {
   compile(project(':react-native-firebase')) {   
       transitive = false
   }
   // ... other dependencies listed
}
Sync gradle. You should be good to go for Android. At this point, we can quickly test notification on Android Device from Firebase Console.
Make sure your application is in background while testing, so that Firebase can automatically deliver to notification tray.
To test on console:
a) Go to Cloud Messaging section on left Pane.
b) Click on Send your first message.
c) Enter text, Select your app in User Segment and click Send Messagebutton. You should be able to receive notification.


iOS Setup
  1. In Project Navigator, right click Libraries > Add Files To <YourProject>. Navigate to <YourProject>/node_modules/react-native-firebase/ios/. Select RNFirebase.xcodeproj and click Add button.


  1. Go to Build Phases. Click on the “+” under “Link Binary With Libraries” to add a new library. Add UserNotifications.framework. This framework is required since iOS 10 for push notifications handling.
  2. Again click “+”, select libRNFirebase.a and add it. If you are unable to find it, clean and build project.
  3. Go to Build Settings, find Header Search Path, double click its value and press “+” button. Add following line there:
$(SRCROOT)/../node_modules/react-native-firebase/ios/RNFirebase
Use “Cmd +Shift + Enter + K” keys to clear cache and then build project. Now firebase dependencies should be recognised by Xcode.
4. In AppDelegate:

#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>
@property (nonatomic, strong) UIWindow *window;
@end
#import "AppDelegate.h"
#import <Firebase.h>
#import "RNFirebaseNotifications.h"
#import "RNFirebaseMessaging.h"
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
//Add these 3 lines
[FIRApp configure];
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
[RNFirebaseNotifications configure];
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"agiledefense"
initialProperties:nil
launchOptions:launchOptions];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
[[RNFirebaseNotifications instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
[[RNFirebaseMessaging instance] didRegisterUserNotificationSettings:notificationSettings];
}
-(void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:response.notification.request.content.userInfo];
completionHandler();
}
@end

Receive Push Notifications
Now lets code the most awaited part… Receiving Notifications! 
Handling Permissions
Before app can get any notifications, it is crucial to ask permission from user specially in iOS. If user didn’t allow your app to receive notifications, it can never get any unless user explicitly change it from Settings.
Open main component of your React Native app, from where basically routing starts i.e. App.js. Modify file as per the snippets below.

import React, {Component} from 'react';
import { AsyncStorage } from 'react-native';
import firebase from 'react-native-firebase';
export default class App extends Component {
async componentDidMount() {
this.checkPermission();
}
//1
async checkPermission() {
const enabled = await firebase.messaging().hasPermission();
if (enabled) {
this.getToken();
} else {
this.requestPermission();
}
}
//3
async getToken() {
let fcmToken = await AsyncStorage.getItem('fcmToken');
if (!fcmToken) {
fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
// user has a device token
await AsyncStorage.setItem('fcmToken', fcmToken);
}
}
}
//2
async requestPermission() {
try {
await firebase.messaging().requestPermission();
// User has authorised
this.getToken();
} catch (error) {
// User has rejected permissions
console.log('permission rejected');
}
}
render() {
return (
<View style={{flex: 1}}>
<Text>Welcome to React Native!</Text>
</View>
);
}
}

In componentDidMount, we check if user has granted permission to receive push notifications.
  1. If permission hasn’t been granted to our app, request user in requestPermission method. If permission is successfully assigned, proceed towards token fetch, otherwise gracefully ignore the fact.
  2. If token was fetched earlier, it can be retrieved from AsyncStorage. If no token is found, request Firebase and save it in AsyncStorage.
Listening Notifications
We are now ready to listen push notification events. Before proceeding, you should know about different types of notifications supported by Firebase.
  • Notification-only messages: These are display messages that are automatically handled by Firebase SDK. Notifications are thrown to device tray.
  • Notification + optional data messages: These are also handled by Firebase SDK. Only difference here is when user taps on notification, your app receives payload associated with that notification.
  • Data only messages: These types of notifications are handled exclusively by app. No notification is thrown on device tray unless app explicitly does so. In iOS, these types of notifications are also termed as ‘Silent Push Notifications’.
For more details, you can read from here.
Add these lines in App.js.

import firebase from 'react-native-firebase';
export default class App extends Component {
async componentDidMount() {
this.checkPermission();
this.createNotificationListeners(); //add this line
}
////////////////////// Add these methods //////////////////////
//Remove listeners allocated in createNotificationListeners()
componentWillUnmount() {
this.notificationListener();
this.notificationOpenedListener();
}
async createNotificationListeners() {
/*
* Triggered when a particular notification has been received in foreground
* */
this.notificationListener = firebase.notifications().onNotification((notification) => {
const { title, body } = notification;
this.showAlert(title, body);
});
/*
* If your app is in background, you can listen for when a notification is clicked / tapped / opened as follows:
* */
this.notificationOpenedListener = firebase.notifications().onNotificationOpened((notificationOpen) => {
const { title, body } = notificationOpen.notification;
this.showAlert(title, body);
});
/*
* If your app is closed, you can check if it was opened by a notification being clicked / tapped / opened as follows:
* */
const notificationOpen = await firebase.notifications().getInitialNotification();
if (notificationOpen) {
const { title, body } = notificationOpen.notification;
this.showAlert(title, body);
}
/*
* Triggered for data only payload in foreground
* */
this.messageListener = firebase.messaging().onMessage((message) => {
//process data message
console.log(JSON.stringify(message));
});
}
showAlert(title, body) {
Alert.alert(
title, body,
[
{ text: 'OK', onPress: () => console.log('OK Pressed') },
],
{ cancelable: false },
);
}
}

I hope comments would be enough to explain all scenarios. You should be now able to receive notifications on both platforms.
In iOS, you need to test on Device to receive notifications. iOS Simulator doesn’t support Push Notifications currently.
In Android, if you want to test on emulator, you need to install Google Play Services package.
React-native-firebase is a nice wrapper over Firebase for react native. However, there are few points you should know:
  1. In Android, if you tap on notification when app is killed, this library won’t be able to catch title and body of notification. Therefore these attributes will be undefined in showAlert function. As a solution, you should also send title and body in data payload of notification.
  2. To this date, Silent Push Notifications on iOS are not supported by this library. I have already opened an issue on their repository.
  3. To listen for data message notifications while the app is in background or killed in Android, you need to implement Headless JS functionality. For details, please refer to this link.
Hope this tutorial saves your time and make FCM integration easy in react native. If it helps you in anyway, don’t forget to clap! Thanks :)

Comments

  1. Great blog.
    The details that you shared regarding React Native Application is really very useful for one and all who is starting work on this tech.. This simple blog with the application development code will help everyone to understand about React Native. I was searching for react native app development company and got his blog.
    Thanks for sharing such a great blog.

    ReplyDelete
  2. Thank you for sharing wonderful information about the react native push notification react native push notifications

    ReplyDelete

Post a Comment

Thank You.

Popular Posts

How I Reduced the Size of My React Native App by 85%

How and Why You Should Do It I borrowed 25$ from my friend to start a Play Store Developer account to put up my first app. I had already created the app, created the assets and published it in the store. Nobody wants to download a todo list app that costs 25mb of bandwidth and another 25 MB of storage space. So today I am going to share with you how I reduced the size of Tet from 25 MB to around 3.5 MB. Size Matters Like any beginner, I wrote my app using Expo, the awesome React Native platform that makes creating native apps a breeze. There is no native setup, you write javascript and Expo builds the binaries for you. I love everything about Expo except the size of the binaries. Each binary weighs around 25 MB regardless of your app. So the first thing I did was to migrate my existing Expo app to React Native. Migrating to React Native react-native init  a new project with the same name Copy the  source  files over from Expo project Install all de...

How to recover data of your Android KeyStore?

These methods can save you by recovering Key Alias and Key Password and KeyStore Password. This dialog becomes trouble to you? You should always keep the keystore file safe as you will not be able to update your previously uploaded APKs on PlayStore. It always need same keystore file for every version releases. But it’s even worse when you have KeyStore file and you forget any credentials shown in above box. But Good thing is you can recover them with certain tricks [Yes, there are always ways]. So let’s get straight to those ways. 1. Check your log files → For  windows  users, Go to windows file explorer C://Users/your PC name/.AndroidStudio1.4 ( your android studio version )\system\log\idea.log.1 ( or any old log number ) Open your log file in Notepad++ or Any text editor, and search for: android.injected.signing and if you are lucky enough then you will start seeing these. Pandroid.injected.signing.store.file = This is  file path where t...

React Native - Text Input

In this chapter, we will show you how to work with  TextInput  elements in React Native. The Home component will import and render inputs. App.js import React from 'react' ; import Inputs from './inputs.js' const App = () => { return ( < Inputs /> ) } export default App Inputs We will define the initial state. After defining the initial state, we will create the  handleEmail  and the  handlePassword  functions. These functions are used for updating state. The  login()  function will just alert the current value of the state. We will also add some other properties to text inputs to disable auto capitalisation, remove the bottom border on Android devices and set a placeholder. inputs.js import React , { Component } from 'react' import { View , Text , TouchableOpacity , TextInput , StyleSheet } from 'react-native' class Inputs extends Component { state = { ...