React Native Push Notification (Updated)
Tags: reactnative, javascript
Published: 2021-12-02 14:56:36

## 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
```

then 

`cd ios && pod install
`

**2) Create a new Firebase project:** 

## ANDROID INSTALLATION 

**a) Follow this link** [Step1](https://firebase.google.com/docs/cloud-messaging/android/client) 
upto _"Add Firebase SDKs to your app"_ section [Link](https://firebase.google.com/docs/cloud-messaging/android/client#add_firebase_sdks_to_your_app)


**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
}

```

**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>
     .....

```

Note: If you are using react-native-splash-screen follow this link [link](https://github.com/zo0r/react-native-push-notification/issues/2017#issuecomment-880460887)


## iOS INSTALLATION 

**a) Follow this link** [Step1](https://firebase.google.com/docs/cloud-messaging/ios/client) 
upto _"Register your app with Firebase"_ section [Link](https://firebase.google.com/docs/cloud-messaging/ios/client#register-app)

**b) Follow this link** [Step2](https://rnfirebase.io/messaging/usage/ios-setup)

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

```
#import <UserNotifications/UNUserNotificationCenter.h>

```
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

```

**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];
}
```
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);
}
```

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

```


## 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>
```

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

```