DEV Community

Ajmal Hasan
Ajmal Hasan

Posted on • Updated on

React Native Push Notification (Updated)

A step-by-step tutorial to help you integrate FCM, local, scheduled notification in your React Native app using react native push notification.

Make sure that you follow each step carefully! Even a single line of code or missed step will lead to endless debugging.

1) What Libraries Are Needed?

yarn add @react-native-firebase/app
yarn add @react-native-firebase/messaging
yarn add react-native-push-notification
yarn add @react-native-community/push-notification-ios
Enter fullscreen mode Exit fullscreen mode

then

cd ios && pod install

2) Create a new Firebase project:

ANDROID INSTALLATION

a) Follow this link Step1
upto "Add Firebase SDKs to your app" section Link

*b) In your android/build.gradle
*
You can use the default values if any value does'nt exist


ext {
    googlePlayServicesVersion = "+" // default: "+"
    firebaseMessagingVersion = "21.1.0" // default: "21.1.0"
    // Other settings
    compileSdkVersion = 30 // default: 23
    buildToolsVersion = "30.0.0" // default: "23.0.1"
    targetSdkVersion = 30 // default: 23
    supportLibVersion = "28.0.3" // default: 23.1.1
}

Enter fullscreen mode Exit fullscreen mode

c) AndroidManifest.xml

    .....
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

    <application ....>
        <!-- Change the value to true to enable pop-up for in foreground on receiving remote notifications (for prevent duplicating while showing local notifications set this to false) -->
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_foreground"
                    android:value="false"/>
        <!-- Change the resource name to your App's accent color - or any other color you want -->
        <meta-data  android:name="com.dieam.reactnativepushnotification.notification_color"
                    android:resource="@color/white"/> <!-- or @android:color/{name} to use a standard color -->

        <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationActions" />
        <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
        <receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.QUICKBOOT_POWERON" />
                <action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
            </intent-filter>
        </receiver>

        <service
            android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationListenerService"
            android:exported="false" >
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
     .....

Enter fullscreen mode Exit fullscreen mode

Note: If you are using react-native-splash-screen follow this link link

iOS INSTALLATION

a) Follow this link Step1
upto "Register your app with Firebase" section Link

b) Follow this link Step2

**c) Update AppDelegate.h
**At the top of the file:

#import <UserNotifications/UNUserNotificationCenter.h>

Enter fullscreen mode Exit fullscreen mode

Then, add the 'UNUserNotificationCenterDelegate' to protocols:

Sample (AppDelegate.h):

#import <React/RCTBridgeDelegate.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UNUserNotificationCenter.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>

@property (nonatomic, strong) UIWindow *window;
@end

Enter fullscreen mode Exit fullscreen mode

*d) Then, in your AppDelegate.m, add the following:
*

#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>
Then, in your AppDelegate implementation, add the following:

// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
 [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
 [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  [RNCPushNotificationIOS didReceiveNotificationResponse:response];
}
Enter fullscreen mode Exit fullscreen mode

And then add the following lines:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  ...
  // Define UNUserNotificationCenter
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;

  return YES;
}

//Called when a notification is delivered to a foreground app.
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
  completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
}
Enter fullscreen mode Exit fullscreen mode

Sample (AppDelegate.m):

#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <React/RCTLinkingManager.h>
#import <RNSplashScreen.h>
//push
#import <Firebase.h>
#import <UserNotifications/UserNotifications.h>
#import <RNCPushNotificationIOS.h>


@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  if ([FIRApp defaultApp] == nil) {
    [FIRApp configure];
  }
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"TestTradeAndExports"
                                            initialProperties:nil];

  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];

  [RNSplashScreen show];

  // Define UNUserNotificationCenter
  UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  center.delegate = self;

  return YES;

}

//push config
// Required for the register event.
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
 [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
// Required for the notification event. You must call the completion handler after handling the remote notification.
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
 [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
// Required for localNotification event
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  [RNCPushNotificationIOS didReceiveNotificationResponse:response];
}

//Called when a notification is delivered to a foreground app.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
  NSDictionary *userInfo = notification.request.content.userInfo;
  //Foreground
  NSLog(@"APP_PUSH from foreground %@", userInfo);

  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:^void (UIBackgroundFetchResult result){}];
  if (@available(iOS 14.0, *)) {
    completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionBanner | UNNotificationPresentationOptionBadge);
  } else {
    completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge);
  }
}
//push



- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *, id> *) options {
  if ([self.authorizationFlowManagerDelegate resumeExternalUserAgentFlowWithURL:url]) {
    return YES;
  }
  return [RCTLinkingManager application:app openURL:url options:options];
 }

@end

Enter fullscreen mode Exit fullscreen mode

USAGE in REACT NATIVE

In App.js call "NotificationHandler" like below:

      <SafeAreaProvider style={{ flex: 1, backgroundColor: Colors.dark_blue }}>
        <Provider store={reduxStore}>
          <PersistGate loading={null} persistor={persistor}>
            <Navigation uriPrefix={prefix} {...pageProps} />
            <NotificationHandler />
          </PersistGate>
        </Provider>
      </SafeAreaProvider>
Enter fullscreen mode Exit fullscreen mode

then
NotiifcationHandler.js

import { Alert, Linking, Platform } from 'react-native';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import PushNotification from 'react-native-push-notification';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
import moment from 'moment';
import messaging from '@react-native-firebase/messaging';
import { STORE_DEVICE_TOKEN_ACTION } from '../store/actions/types';
import NavigationService from '../helpers/NavigationService';
import { useState } from 'react';

const NotificationHandler = () => {
  const {} = useSelector(state => ({}), shallowEqual);
  const dispatch = useDispatch();
  const [flag, setflag] = useState(1);

  const navigateFromNotificationFunc = data => {
    setTimeout(
      () => {
        NavigationService?.navigate('NotificationScreen');
      },
      Platform.OS === 'ios' && !data.foreground ? 2000 : 0,
    );

    // switch (+id) {
    //   case 1:
    //       NavigationService.navigate('StartHeatingTime')
    //     break;
    //   default:
    //     break;
    // }
  };

  // Must be outside of any component LifeCycle (such as `componentDidMount`).
  PushNotification.configure({
    // (optional) Called when Token is generated (iOS and Android)
    onRegister: function (token) {
      requestUserPermission(dispatch);
    },

    // (required) Called when a remote is received or opened, or local notification is opened
    onNotification: function (notification) {
      console.log('NOTIFICATION:', notification);

      // alert(JSON.stringify(notification));

      //if notification not clicked display popup
      //if clicked navigate to screen if exist
      const clicked = notification.userInteraction;
      if (clicked) {
        navigateFromNotificationFunc(
          Platform.OS === 'ios' ? notification : notification.data,
        );
      } else {
        if (flag === 1) {
          LocalNotification(notification);
          setflag(0);
        }
        setTimeout(() => {
          setflag(1);
        }, 500);
      }
      // (required) Called when a remote is received or opened, or local notification is opened
      notification.finish(PushNotificationIOS.FetchResult.NoData);
    },

    // (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
    onAction: function (notification) {
      console.log('ACTION:', notification.action);
      console.log('NOTIFICATION:', notification);
      // process the action
    },

    // (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
    // onRegistrationError: function(err) {
    //  console.error(err.message, err);
    // },

    // IOS ONLY (optional): default: all - Permissions to register.
    permissions: {
      alert: true,
      badge: true,
      sound: true,
    },
    popInitialNotification: true,

    largeIcon: 'ic_launcher',
    smallIcon: 'ic_launcher',

    // Should the initial notification be popped automatically
    // default: true
    // senderID: '68549140594',
    // requestPermissions: true

    /**
     * (optional) default: true
     * - Specified if permissions (ios) and token (android and ios) will requested or not,
     * - if not, you must call PushNotificationsHandler.requestPermissions() later
     * - if you are not using remote notification or do not have Firebase installed, use this:
     *     requestPermissions: Platform.OS === 'ios'
     */
  });
  return null;
};

const CancelLocalNotifications = id => {
  PushNotification.cancelLocalNotification({ id: id + '' });
};

const LocalNotification = data => {
  PushNotification.localNotification({
    title: data?.data?.title, // (optional)
    message: data?.data?.message, // (required)
    playSound: true, // (optional) default: true
    soundName: 'default', // (optional) Sound to play when the notification is shown. Value of 'default' plays the default sound. It can be set to a custom sound such as 'android.resource://com.xyz/raw/my_sound'. It will look for the 'my_sound' audio file in 'res/raw' directory and play it. default: 'default' (default sound is played)
  });
};

// const LocalNotificationSchedule = data => {
//   console.log('LocalNotificationSchedule', data);
//   PushNotification.localNotificationSchedule({
//     //... You can use all the options from localNotifications
//     title: 'Hye',
//     message: 'MESSAGE', // (required)
//     date: new Date(Date.now() + 1000), // in n secs
//     vibration: 10000, //in milliseconds
//     allowWhileIdle: true,
//     visibility: 'public',
//     priority: 'max',
//     ignoreInForeground: false,
//     autoCancel: false,
//   });
// };

const requestUserPermission = async dispatch => {
  const authStatus = await messaging().requestPermission();
  const enabled =
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL;
  if (enabled) {
    console.log('Notification Authorization status:', authStatus);
    getAPNSToken(dispatch);
    getFcmToken(dispatch);
  }
  //  else {
  //   Alert.alert(
  //     'Please Enable Notification',
  //     '',
  //     [
  //       {
  //         text: 'Settings',
  //         onPress: () => {
  //           Linking.openURL('app-settings:');
  //         },
  //       },
  //     ],
  //     { cancelable: false },
  //   );
  // }
};

const getFcmToken = async dispatch => {
  const fcmToken = await messaging().getToken();
  if (fcmToken) {
    console.log('Notification Device token ', fcmToken);
    dispatch({ type: STORE_DEVICE_TOKEN_ACTION, payload: fcmToken });
  } else {
    console.log('Notification failed token', fcmToken);
  }
};

const getAPNSToken = async () => {
  const apnsToken = await messaging().getAPNSToken();
  console.log('Notification Device token APNS', apnsToken);
  // Alert.alert(apnsToken)
  // if (apnsToken) {
  //   console.log('Notification Device token ', apnsToken);
  //   await AsyncStorage.setItem('apnsToken', apnsToken);
  // } else {
  //   console.log('Notification failed token', apnsToken);
  // }
};

export { NotificationHandler, LocalNotification };

Enter fullscreen mode Exit fullscreen mode

Discussion (1)

Collapse
nsengiyunva profile image
King Isaac Nsengiyunva

github link?