ios: iOS Background Notification Not Working

Bug report

Summary

I am trying to acheive Push Notifications for both Android & iOS (Foreground & Background). I followed the official firebase docs and was able to run it correctly on Android in all app states. Foreground, Background & Quit it works

But the same thing is only working in Foreground for IOS.

In background what i want is push notification from RestApi. And on foreground I am showing a LocalNotification which is working fine.

No Matter what I do it doesnot trigger the messaging().setBackgroundMessageHandler(async remoteMessage => {console.log(‘Message handled in the background!’, remoteMessage);});

Environment info

react-native info output:

System:
    OS: Windows 10 10.0.18363
    CPU: (8) x64 Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz
    Memory: 2.67 GB / 11.88 GB
  Binaries:
    Node: 12.16.1 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.21.1 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
    npm: 6.13.4 - C:\Program Files\nodejs\npm.CMD
    Watchman: Not Found
  SDKs:
    Android SDK:
      API Levels: 14, 15, 16, 17, 18, 19, 21, 22, 23, 24, 25, 26, 27, 28, 29
      Build Tools: 24.0.2, 25.0.3, 26.0.1, 26.0.2, 28.0.2, 28.0.3, 29.0.0, 29.0.2
      System Images: android-19 | Google APIs Intel x86 Atom, android-27 | Google APIs Intel x86 Atom, android-27 | Google Play Intel x86 Atom, android-28 | Google APIs Intel x86 Atom, android-28 | Google Play Int
el x86 Atom, android-29 | Google APIs Intel x86 Atom
      Android NDK: Not Found
  IDEs:
    Android Studio: Version  3.5.0.0 AI-191.8026.42.35.6010548
  Languages:
    Java: 1.8.0_211 - C:\Program Files\Java\jdk1.8.0_211\bin\javac.EXE
    Python: 2.7.17 - C:\Python27\python.EXE
  npmPackages:
    @react-native-community/cli: Not Found
    react: 16.11.0 => 16.11.0
    react-native: 0.62.2 => 0.62.2
  npmGlobalPackages:
    *react-native*: Not Found

`Package.json’

{
  "name": "ABCAPP",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "lint": "eslint ."
  },
  "dependencies": {
    "@react-native-community/masked-view": "0.1.5",
    "@react-native-community/push-notification-ios": "^1.2.2",
    "@react-native-firebase/app": "^7.2.1",
    "@react-native-firebase/iid": "^7.1.4",
    "@react-native-firebase/messaging": "^7.1.5",
    "@react-navigation/drawer": "^5.1.1",
    "@react-navigation/material-bottom-tabs": "^5.1.6",
    "@react-navigation/native": "5.2.1",
    "@react-navigation/stack": "5.2.16",
    "axios": "^0.19.2",
    "expo-font": "^8.1.1",
    "native-base": "2.13.8",
    "react": "16.11.0",
    "react-dom": "~16.9.0",
    "react-native": "0.62.2",
    "react-native-gesture-handler": "~1.5.0",
    "react-native-notifications": "^3.2.2",
    "react-native-reanimated": "~1.4.0",
    "react-native-safe-area-context": "0.6.0",
    "react-native-screens": "2.0.0-alpha.12",
    "react-native-unimodules": "^0.9.1",
    "react-native-vector-icons": "^6.6.0",
    "react-native-web": "~0.11.7",
    "react-redux": "^7.2.0",
    "redux": "^4.0.5",
    "redux-logger": "^3.0.6",
    "redux-persist": "^6.0.0",
    "redux-saga": "^1.1.3"
  },
  "devDependencies": {
    "@babel/core": "^7.6.2",
    "@babel/runtime": "^7.6.2",
    "@react-native-community/eslint-config": "^0.0.5",
    "babel-jest": "^24.9.0",
    "eslint": "^6.5.1",
    "jest": "^24.9.0",
    "metro-react-native-babel-preset": "^0.58.0",
    "react-test-renderer": "16.11.0"
  },
  "jest": {
    "preset": "react-native"
  }
}

Reproducible sample code

import React, {useEffect, useState} from 'react';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware, compose} from 'redux';
import logger from 'redux-logger';
import createSagaMiddleware from 'redux-saga';
import AllSagas from './js/sagas';
import {persistStore, persistReducer} from 'redux-persist';
import {AsyncStorage, View, Image , Platform } from 'react-native';
import {PersistGate} from 'redux-persist/integration/react';
import reducer from './js/reducers';
import RootNavigator from './js/navigators/RootNavigator';
import messaging, {AuthorizationStatus} from '@react-native-firebase/messaging';
import {firebase} from '@react-native-firebase/iid';
import {Alert} from 'react-native';
import {Notifications} from 'react-native-notifications';
import PushNotificationIOS from "@react-native-community/push-notification-ios";


const persistConfig = {
  key: 'root',
  storage: AsyncStorage,
};

const persistedReducer = persistReducer(persistConfig, reducer);
let sagaMiddleware = createSagaMiddleware();
const middleware = [sagaMiddleware];

function configureStore(initialState) {
  const enhancer = compose(applyMiddleware(...middleware, logger));
  return createStore(persistedReducer, initialState, enhancer);
}

const store = configureStore({});
sagaMiddleware.run(AllSagas);
const persistor = persistStore(store);

// Uncomment in case you want to delete state from persistant storage.
// persistor.purge();

export default App = () => {

  const [isReady, setisReady] = useState(false);

  requestUserPermission();
  getMyToken();

  // Foreground Notification Listener;
  useEffect(() => {
    const unsubscribe = messaging().onMessage(async remoteMessage => {
      // Alert.alert('A new FCM message arrived!', JSON.stringify(remoteMessage));

      const { title , body } = remoteMessage.data;

      if(Platform.OS === 'android'){

        Notifications.postLocalNotification({
          body: body,
          title: title,
          sound: 'default',
          silent: false,
          category: 'SOME_CATEGORY',
          userInfo: {},
          'google.message_id': '',
          payload: {
            body: body,
            title: title,
            sound: 'default',
            silent: false,
            category: 'SOME_CATEGORY',
            'google.message_id': '',
          },
        });

      }else if(Platform.OS==='ios'){

        PushNotificationIOS.presentLocalNotification({
          
          alertTitle: title,
          alertBody: body

        });

      }
    });
    return unsubscribe;
  },[]);


  setTimeout(() => {
    setisReady(true);
  }, 3500);

  if (!isReady) {
    return (
        <View style={{flex: 1}}>
          <Image
              source={require('./assets/images/gif_splash.gif')}
              resizeMethod="auto"
              resizeMode="cover"
              style={{width: '100%', height: '100%'}}
          />
        </View>
    );
  }

  return (
      <Provider store={store}>
        <PersistGate loading={null} persistor={persistor}>
          <RootNavigator/>
        </PersistGate>
      </Provider>
  );

};


async function requestUserPermission() {
  const authStatus = await messaging().requestPermission({
  alert: true,
  announcement: false,
  badge: true,
  carPlay: true,
  provisional: false,
  sound: true,
});
  const enabled =
    authStatus === AuthorizationStatus.AUTHORIZED ||
    authStatus === AuthorizationStatus.PROVISIONAL;

  if (enabled) {
    console.log('Authorization status:', authStatus);
  }
}

const getMyToken = async () => {
  const alarmFcmToken = await firebase.messaging().getToken(
    firebase.app().options.messagingSenderId, // default to this app
    'FCM',
  ); // defaults to 'FCM');

  console.log('TOOOOOOOOOOOOOOKEEEEEEEEEEEN');
  console.log(alarmFcmToken);
};

/**
 * @format
 */

import React from 'react';
import {AppRegistry, Platform} from 'react-native';
import {Alert} from 'react-native';
import messaging from '@react-native-firebase/messaging';
import {Notifications} from 'react-native-notifications';

import App from './App';
import {name as appName} from './app.json';

// Register background handler
messaging().setBackgroundMessageHandler(async remoteMessage => {
  console.log('Message handled in the background!', remoteMessage);
});

function HeadlessCheck({ isHeadless }) {
  if (isHeadless) {
    // App has been launched in the background by iOS, ignore
    return null;
  }

  return <App />;
}

AppRegistry.registerComponent(appName, () => HeadlessCheck);

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

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

@property (nonatomic, strong) UIWindow *window;

@end

#import "AppDelegate.h"

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>

#if DEBUG
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.h>
#import <RNCPushNotificationIOS.h>
#import <UserNotifications/UNUserNotificationCenter.h>
#import <Firebase.h>

static void InitializeFlipper(UIApplication *application) {
  FlipperClient *client = [FlipperClient sharedClient];
  SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults];
  [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]];
  [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]];
  [client addPlugin:[FlipperKitReactPlugin new]];
  [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]];
  [client start];
}
#endif

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  
#if DEBUG
  InitializeFlipper(application);
#endif
  
  
  // Start I added
    [FIRApp configure];
    
    if ([UNUserNotificationCenter class] != nil) {
      // iOS 10 or later
      // For iOS 10 display notification (sent via APNS)
      [UNUserNotificationCenter currentNotificationCenter].delegate = self;
      UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert |
          UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
      [[UNUserNotificationCenter currentNotificationCenter]
          requestAuthorizationWithOptions:authOptions
          completionHandler:^(BOOL granted, NSError * _Nullable error) {
            // ...
          }];
    } else {
      // iOS 10 notifications aren't available; fall back to iOS 8-9 notifications.
      UIUserNotificationType allNotificationTypes =
      (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
      UIUserNotificationSettings *settings =
      [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
      [application registerUserNotificationSettings:settings];
    }

    [application registerForRemoteNotifications];
    
    [FIRMessaging messaging].delegate = self;
    
    [[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult * _Nullable result,
                                                        NSError * _Nullable error) {
      if (error != nil) {
        NSLog(@"Error fetching remote instance ID: %@", error);
      } else {
        NSLog(@"Remote instance ID token: %@", result.token);
      }
    }];
    
    [FIRMessaging messaging].autoInitEnabled = YES;

  // End I added
  
  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"HomeMediCare"
                                            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];
  return YES;
}

// Start I added

- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
    NSLog(@"FCM registration token: %@", fcmToken);
    // Notify about received token.
    NSDictionary *dataDict = [NSDictionary dictionaryWithObject:fcmToken forKey:@"token"];
    [[NSNotificationCenter defaultCenter] postNotificationName:
     @"FCMToken" object:nil userInfo:dataDict];
}

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  [FIRMessaging messaging].APNSToken = deviceToken;
  [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}

- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
  [RNCPushNotificationIOS didRegisterUserNotificationSettings:notificationSettings];
}

// 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
{
  [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
  [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}

// Required for the registrationError event.
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
  [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}

// Required for the localNotification event.
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
  [RNCPushNotificationIOS didReceiveLocalNotification:notification];
}

// 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;
    [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
    completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
  }

// End I added

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

@end

Library version: ^1.2.2 iOS: 13.4.1

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 7
  • Comments: 19

Most upvoted comments

Is this module compatible with react native firebase cloud messaging ?

same issue with me and the same version of Firebase V7

Experiencing this also. No background notification being sent when soft-closing the app.

Just an FYI as this is pretty difficult to debug due to the way messaging works and the fact you will get different message/system behaviour depending on the message structure. Below works fairly consistently Note that i don’t have a data object.

{
    "to": "{{myID}}",
    "notification": {
        "title": "Working Good",
        "body": "backgroudn noti",
        "badge": "1"
    },
    "priority": "high",
    "badge": "1",
    "content_available": true
}

Definitely doesn’t trigger background notifications. Doesn’t contain the priority, badge, and content_available properties. This was my original message structure.

{
    "to": "myID",
    "notification": {
        "title": "payload.title1",
        "body": "payload.body"
    },
    "data": {
        "link": "payload.link"
    },
    "apns": {
        "payload": {
            "aps": {
                "mutable-content": 1
            }
        }
    }
}

Might trigger background notifications and badge count. But not always I’m still debuggin issues myself, and i’m actually using a different notification package, but this was such a nightmare, that i fellt the need to share.

Note, that here i have the priority fields and both a notification and data block, i’ve read in other posts for react native that if you have a notification “AND” a data block that firebase messagings setbackgroundhandler won’t trigger. if your using that. But haven’t confirmed that yet.

{
    "to": "myID",
    "notification": {
        "title": "payload.title",
        "body": "payload.body"
    },
    "data": {
        "link": "payload.link"
    },
    "apns": {
        "payload": {
            "aps": {
                "mutable-content": 1
            }
        }
    },
    "priority":"high",
    "badge":"1",
    "content_available":true
}

Anyway, my advice if this is a poblem, is to setup postman or some test endpoints with all the possible combinations of message.

I was able to just use the react-native-firebase latest without this push notification library.

Please make sure you send the data correctly in order to trigger the notification.

At my side, I have used the following format:

{ “to”: “TOKEN”, “data”:{ “title”:“Some title”, “message”:“Some text”, “vibrate”:1, “sound”:1, “channelId”: “TEST” }, “notification”: {. <-- Trigger notification “title”:“Some title”, “body”:“Some text”, “sound”: “default” }, “content_available”:true, <-- Trigger notification required for IOS “priority”:“high” }

setBackgroundMessageHandler just handle the message once the app opens due to the notification. It does not trigger when notification is received.

I can vouch iOS isn’t working for me either, I have been stuck with debugging for like 2 days now