Hi guys,
I was working with "@react-native-firebase/app": "^8.4.1" and "@react-native-firebase/messaging": "^7.8.4", few days ago and everything was fine, except for setBackgroundHandler that never was called when app is closed.
Yesterday, I updated my dependencies searching some solution to handler problem but now I'm stunned: setBackgroundHandler doesn't work at any case. No when app is on background and less when it is killed.
Today, I've made a new cleaning project with only RNFB dependencies, but this problem persists. Maybe, I'm on the wrong way. Please check my files.
Click To Expand
#### `package.json`:
# N/A
{
"name": "notisimple",
"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 .",
"build:ios": "react-native bundle --entry-file='index.js' --bundle-output='./ios/main.jsbundle' --dev=false --platform='ios'"
},
"dependencies": {
"@react-native-firebase/app": "^8.4.5",
"@react-native-firebase/messaging": "^7.9.0",
"react": "16.13.1",
"react-native": "0.63.3"
},
"devDependencies": {
"@babel/core": "^7.8.4",
"@babel/runtime": "^7.8.4",
"@react-native-community/eslint-config": "^1.1.0",
"babel-jest": "^25.1.0",
"eslint": "^6.5.1",
"jest": "^25.1.0",
"metro-react-native-babel-preset": "^0.59.0",
"react-test-renderer": "16.13.1"
},
"jest": {
"preset": "react-native"
}
}
#### `index.js`:
# N/A
import React from 'react'
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
import messaging from '@react-native-firebase/messaging';
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
console.log('let me know');
});
function HeadlessCheck({ isHeadless }){
console.log(isHeadless);
if (isHeadless) {
return null;
}
return <App/>;
}
AppRegistry.registerComponent(appName, ()=> HeadlessCheck);
#### `App.tsx`:
import React, {
useEffect,
useState
} from 'react';
import { AppState,View } from 'react-native'
import ServiceFCM from './src/functions/FCM.service';
let onLoad = false;
const App: React.FC = () => {
const [onNotificationRecieved, setNofiticationRecieved] = useState(
false,
);
const [notificationBehaviour, setNotificationBehaviour] = useState(
'',
);
const [permissions, setPermissions] = useState({});
useEffect(() => {
onLoad = false;
async function init() {
ServiceFCM.requestOnlyUserPermission().then(async (auth) => {
await ServiceFCM.registerDeviceAPNS(auth);
await ServiceFCM.registerFCM(onRegister, onNoti, onOpenNoti);
});
function onRegister(token) {
}
function onNoti(remoteMessage) {
console.log(remoteMessage, 'llegadaNoti', AppState.currentState);
}
async function onOpenNoti(noti) {
console.log(noti, 'abierto');
}
return;
}
if (!onLoad) {
onLoad = true;
init().then((x) => {
}).catch((err) => {
}).finally(() => {
});
}
return () => {
};
}, [notificationBehaviour]);
return (
<>
<View />
</>
);
};
export default App;
#### `src/functions/FCM.services.ts`:
# N/A
import messaging from '@react-native-firebase/messaging';
import { Platform } from 'react-native';
import { callFunction } from './UtilNet.service';
class ServiceFCM {
registerDeviceAPNS = async (auth) => {
if (Platform.OS === 'ios') {
try {
let atl = await messaging().registerDeviceForRemoteMessages();
console.log('Register APNS Success', auth ? auth.toString() : '(Re requerired)');
await messaging().setAutoInitEnabled(true);
let response = await callFunction('APN Token', {
actioner: async () => {
return await messaging().getAPNSToken();
},
successCondition: (resp) => {
return resp !== null;
},
attemps: 4
});
}
catch (err) {
console.log('Register APNS / Error Message ', err.message);
}
}
};
registerFCM = async (
onRegister,
onNotification,
onOpenNotification
) => {
await this.checkPermission(onRegister);
messaging().onTokenRefresh(async (fcmToken) => {
console.log('token invalidado');
});
messaging().onNotificationOpenedApp(async (remoteMessage) => {
// writeDebug('Abierto Noti', remoteMessage);
await onOpenNotification(remoteMessage);
});
messaging()
.getInitialNotification()
.then((remoteMessage) => {
if (remoteMessage) {
}
});
messaging().onMessage((remoteMessage) => {
if (remoteMessage) {
onNotification(remoteMessage);
}
});
};
refreshToken = async () => {
await messaging().deleteToken();
await this.requestUserPermission();
};
requestUserPermission = async (onRegister) => {
let userPermission = await messaging().requestPermission();
console.log('Permiso cedido', userPermission);
if (userPermission == messaging.AuthorizationStatus.AUTHORIZED || userPermission == messaging.AuthorizationStatus.PROVISIONAL)
await this.getFcmToken(onRegister);
};
requestOnlyUserPermission = async () => {
return messaging().requestPermission();
};
getFcmToken = async (onRegister) => {
let fcmToken = null;
try {
fcmToken = await messaging().getToken();
}
catch (err) {
console.log('Get Token FCM / Error Message ', err.message);
}
if (fcmToken) {
console.log('Token en Memoria', fcmToken);
} else {
console.log('Token FCM', 'No token received');
}
};
getCurrentToken = async () => {
let token = await messaging().getAPNSToken();
return token;
}
checkPermission = async (onRegister) => {
try {
let enabled = await messaging().hasPermission();
console.log('Tiene Permiso Noti', enabled);
if (enabled == messaging.AuthorizationStatus.AUTHORIZED || enabled == messaging.AuthorizationStatus.PROVISIONAL) {
await this.getFcmToken(onRegister);
} else {
await this.requestUserPermission(onRegister);
}
} catch (err) {
console.log('Permission/ Error Message ', err.message);
}
};
}
export default new ServiceFCM();
#### `src/functions/UtilNet.service.ts`
let TIME_OUT = 60000;
type ConfigCallFunction = {
actioner: any,
successCondition: any,
attemps?: number
}
export const callFunction = async function (name: string, config: ConfigCallFunction) {
if (!(config.attemps > 0))
config.attemps = 1;
let currentAttemp = 0;
let response = null;
let formatAttemp = () => {
return config.attemps > 1 ? `Att. ${currentAttemp + 1}` : '';
}
let start = null, finish = null;
let callFunctions = async () => {
try {
start = Date.now();
response = await config.actioner();
finish = Date.now() - start;
}
catch (err) {
if (!finish)
finish = Date.now() - start;
}
return response;
}
while (currentAttemp < config.attemps) {
if (config.successCondition(await callFunctions()))
break;
currentAttemp++;
}
return response;
}
### iOS
#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like:
# N/A
require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
platform :ios, '10.0'
target 'notisimple' do
config = use_native_modules!
use_react_native!(:path => config["reactNativePath"])
target 'notisimpleTests' do
inherit! :complete
# Pods for testing
end
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
use_flipper!
post_install do |installer|
flipper_post_install(installer)
end
end
target 'notisimple-tvOS' do
# Pods for notisimple-tvOS
target 'notisimple-tvOSTests' do
inherit! :search_paths
# Pods for testing
end
end
#### `AppDelegate.m`:
// N/A
#import "AppDelegate.h"
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import <Firebase.h>
#import "RNFBMessagingModule.h"
#if RCT_DEV
#import <React/RCTDevLoadingView.h>
#endif
#ifdef FB_SONARKIT_ENABLED
#import <FlipperKit/FlipperClient.h>
#import <FlipperKitLayoutPlugin/FlipperKitLayoutPlugin.h>
#import <FlipperKitUserDefaultsPlugin/FKUserDefaultsPlugin.h>
#import <FlipperKitNetworkPlugin/FlipperKitNetworkPlugin.h>
#import <SKIOSNetworkPlugin/SKIOSNetworkAdapter.h>
#import <FlipperKitReactPlugin/FlipperKitReactPlugin.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
{
#ifdef FB_SONARKIT_ENABLED
InitializeFlipper(application);
#endif
[FIRApp configure];
NSDictionary *appProperties = [RNFBMessagingModule addCustomPropsToUserProps:nil withLaunchOptions:launchOptions];
#ifdef DEBUG
NSURL *jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
NSURL *jsCodeLocation = [CodePush bundleURL];
#endif
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:jsCodeLocation
moduleProvider:nil
launchOptions:launchOptions];
#if RCT_DEV
[bridge moduleForClass:[RCTDevLoadingView class]];
#endif
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"notisimple"
initialProperties:appProperties];
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;
}
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}
@end
Click To Expand
**`react-native info` output:**
System:
OS: macOS 10.15.7
CPU: (4) x64 Intel(R) Core(TM) i7-8550U CPU @ 1.80GHz
Memory: 411.21 MB / 11.32 GB
Shell: 5.7.1 - /bin/zsh
Binaries:
Node: 14.9.0 - /usr/local/bin/node
Yarn: Not Found
npm: 6.14.8 - /usr/local/bin/npm
Watchman: 4.9.0 - /usr/local/bin/watchman
Managers:
CocoaPods: 1.9.3 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: iOS 14.0, DriverKit 19.0, macOS 10.15, tvOS 14.0, watchOS 7.0
Android SDK:
API Levels: 29, 30
Build Tools: 28.0.3, 29.0.2, 30.0.2
System Images: android-29 | Intel x86 Atom_64, android-29 | Google APIs Intel x86 Atom, android-29 | Google APIs Intel x86 Atom_64
Android NDK: Not Found
IDEs:
Android Studio: 4.0 AI-193.6911.18.40.6626763
Xcode: 12.0.1/12A7300 - /usr/bin/xcodebuild
Languages:
Java: 1.8.0_265 - /usr/bin/javac
Python: 2.7.16 - /usr/bin/python
npmPackages:
@react-native-community/cli: Not Found
react: 16.13.1 => 16.13.1
react-native: 0.63.3 => 0.63.3
react-native-macos: Not Found
npmGlobalPackages:
*react-native*: Not Found
- **Platform that you're experiencing the issue on**:
- [ ] iOS
- [ ] Android
- [x] **iOS** but have not tested behavior on Android
- [ ] **Android** but have not tested behavior on iOS
- [ ] Both
- **`react-native-firebase` version you're using that has this issue:**
- `8.4.5`
- **`Firebase` module(s) you're using that has the issue:**
- `Messaging`
- **Are you using `TypeScript`?**
- `Yes` & `4.0.3`
React Native Firebase and Invertase on Twitter for updates on the library.I can't reproduce this on android or iOS - I just tested both with current stable versions of everything
android with a data-only push in foreground, background and headless
iOS in foreground and background/quit
all on real devices, everything works
I see that you've got all this registration and permission stuff. I'm aware you need to ask for permission as a hard requirement, but the rest of it should all be default settings, so you should just need to get the token and use it
You may need the results of https://github.com/invertase/react-native-firebase/issues/4299#issuecomment-705128266 to get them on a fresh install (or you'll need to force quit and restart app)
"@react-native-firebase/analytics": "^7.6.7",
"@react-native-firebase/app": "^8.4.5",
"@react-native-firebase/auth": "^9.3.0",
"@react-native-firebase/crashlytics": "^8.4.9",
"@react-native-firebase/dynamic-links": "^7.5.9",
"@react-native-firebase/firestore": "^7.8.6",
"@react-native-firebase/functions": "^7.4.8",
"@react-native-firebase/iid": "^7.4.8",
"@react-native-firebase/in-app-messaging": "^7.5.6",
"@react-native-firebase/messaging": "^7.9.0",
"@react-native-firebase/perf": "^7.4.8",
"@react-native-firebase/remote-config": "^9.0.10",
"@react-native-firebase/storage": "^7.4.9",
I can't reproduce this on android or iOS - I just tested both with current stable versions of everything
android with a data-only push in foreground, background and headless
iOS in foreground and background/quitall on real devices, everything works
I see that you've got all this registration and permission stuff. I'm aware you need to ask for permission as a hard requirement, but the rest of it should all be default settings, so you should just need to get the token and use it
You may need the results of #4299 (comment) to get them on a fresh install (or you'll need to force quit and restart app)
"@react-native-firebase/analytics": "^7.6.7", "@react-native-firebase/app": "^8.4.5", "@react-native-firebase/auth": "^9.3.0", "@react-native-firebase/crashlytics": "^8.4.9", "@react-native-firebase/dynamic-links": "^7.5.9", "@react-native-firebase/firestore": "^7.8.6", "@react-native-firebase/functions": "^7.4.8", "@react-native-firebase/iid": "^7.4.8", "@react-native-firebase/in-app-messaging": "^7.5.6", "@react-native-firebase/messaging": "^7.9.0", "@react-native-firebase/perf": "^7.4.8", "@react-native-firebase/remote-config": "^9.0.10", "@react-native-firebase/storage": "^7.4.9",
I'm testing in a real device like iPhone 5s. I'll get the correct token and I use it with postman. Foreground and background notifications works fine, but.. setBackgroundMessageHandler never is called when app is in background.
this is my json notification:
{
"data": {
"rp_campaign_id": "36-001",
"rp_sequential_id": "36-001",
"rp_notification_id": "20200921045058910"
},
"notification": {
"title": "Notificaci贸n de Prueba",
"body": "Saludos Cordiales"
},
"priority": "high",
"to" : "cNG1XorNNEVXla6lyVvvPl:APA91bH6drg8pd05Cksk4CIgb9l2Mv-WEGs1A7-vA2uqa1MzvTvbmzK4T-2FidY6bge-ttlY3wJ2nvqAtmdyPFglAQYHYIK1qg_uHr63ZiLqksS8w7c5TTpr9QKI9KtaWmng9FV6zWZJ"
}
Edit: Now, I'm testing your additions on my dependencies code.
missing content-available
https://rnfirebase.io/messaging/usage#data-only-messages
also if you send mixed notifications + data messages the SDK will handle them because of the notification payload, so it will likely go to the ios notification center and you will get no control until user taps
for this reason I do data-only, it cleans up the matrix of payload types / ways things are handled
also if you send mixed notifications + data messages the SDK will handle them because of the notification payload, so it will likely go to the ios notification center and you will get no control until user taps
for this reason I do data-only, it cleans up the matrix of payload types / ways things are handled
Hi Mike,
I could solve the problem, my error was not provide the content-available property at this format: "content_available" : true, and seems that this property is strictily neccesary to call setBackgroundMessageHandler when i'm sending mixed notifications. It's fine in case when is a clean project.
By other hand, I'm mixing react-native-notification-push library with this library, because I need to show local notifications in foreground, but it seems that in some way that library is causing lose setbackgroundMessageHandler. So I decided to remove it, and modify the RNFB event where foreground event onReceiveMessage is completed, to show a local notification in foreground. Now it works without use any other third-party library :).
Finally, about to call functions or made tasks when App is terminated, is it possible? I was reading about that and I found diverses responses. I want to implement it but i don't know if maybe it's a system limitation.
Update: I was able to receive notifications and make additional functionality, even when app it's closed, at this case I used Notification Service Extension with AppGroups to make call functions and share data between the service and main app.
Hello @game8149, when you mentioned app it's "closed". Do you actually "SWIPE the app away" from app switcher?
Hello @game8149, when you mentioned app it's "closed". Do you actually "SWIPE the app away" from app switcher?
Hi, yes. I can make additional functionality with Notification Service Extension
@game8149 can you describe how you handle the payload with Notification Service Extension + App Group while you app is killed (Swiped away)? Do you still rely on setBackgroundMessageHandler method or what? From what I've understand and tested so far, none of the code is being triggered after app is killed until user launch the app again.
Appreciate and thanks in advance! 馃檹
@game8149 can you describe how you handle the payload with Notification Service Extension + App Group while you app is killed (Swiped away)? Do you still rely on setBackgroundMessageHandler method or what? From what I've understand and tested so far, none of the code is being triggered after app is killed until user launch the app again.
Appreciate and thanks in advance! 馃檹
Well, yeah, but only with native code, not js code. I could send a API call request to update the notification state on my external service. If you need info about implementation, message me.
Most helpful comment
Hi Mike,
I could solve the problem, my error was not provide the content-available property at this format: "content_available" : true, and seems that this property is strictily neccesary to call setBackgroundMessageHandler when i'm sending mixed notifications. It's fine in case when is a clean project.
By other hand, I'm mixing react-native-notification-push library with this library, because I need to show local notifications in foreground, but it seems that in some way that library is causing lose setbackgroundMessageHandler. So I decided to remove it, and modify the RNFB event where foreground event onReceiveMessage is completed, to show a local notification in foreground. Now it works without use any other third-party library :).
Finally, about to call functions or made tasks when App is terminated, is it possible? I was reading about that and I found diverses responses. I want to implement it but i don't know if maybe it's a system limitation.