mirror of
https://github.com/zhigang1992/react-native-firebase.git
synced 2026-04-18 08:20:47 +08:00
fix(messaging,ios): keep original UNUserNotificationCenter dele… (#3427)
* fix(messaging,ios): keep original UNUserNotificationCenter delegate Keeps a reference to any original UNUserNotificationCenter delegates that are set before we replace the delegate with out own implementation. Internally we will also call the original delegate if our code does not already handle the delegate call. This should keep compatibility with other RN modules that set the delegate. * v6.4.1-alpha.0 * Revert "v6.4.1-alpha.0" This reverts commit b355a86a * feat: automatically register with APNs * docs: typos * fix: forward delegate call to FIRAuth Fixes / supersedes #3425 * fix(messaging): add activity check to getInitialNotification (#3495) * fix(messaging): add activity check to getInitialNotification * fix(messaging): add activity check to getInitialNotification * Update .spellcheck.dict.txt Co-authored-by: Mike Diarmid <mike.diarmid@gmail.com> Co-authored-by: Elliot Hesp <elliot.hesp@gmail.com>
This commit is contained in:
@@ -6,7 +6,7 @@ Analytics
|
||||
analytics
|
||||
APIs
|
||||
APIs.
|
||||
APNS
|
||||
APNs
|
||||
async
|
||||
auth
|
||||
Auth
|
||||
@@ -123,3 +123,4 @@ v6
|
||||
VSCode
|
||||
Wix
|
||||
Xcode
|
||||
lifecycle
|
||||
|
||||
@@ -164,6 +164,9 @@ function App() {
|
||||
}
|
||||
```
|
||||
|
||||
The call to `getInitialNotification` should happen within a React lifecycle method after mounting (e.g. `componentDidMount` or `useEffect`).
|
||||
If it's called too soon (e.g. within a class constructor or global scope), the notification data may not be available.
|
||||
|
||||
# Notifee - Advanced Notifications
|
||||
|
||||
FCM provides support for displaying basic notifications to users with minimal integration required. If however you require
|
||||
|
||||
@@ -22,8 +22,8 @@ yarn add @react-native-firebase/messaging
|
||||
cd ios/ && pod install
|
||||
```
|
||||
|
||||
> iOS requires further configuration steps to be carried out before you can start receiving and sending
|
||||
> messages through Firebase. Read the documentation on how to [setup iOS with Firebase Messaging](/messaging/usage/ios-setup).
|
||||
> iOS requires further configuration before you can start receiving and sending
|
||||
> messages through Firebase. Read the documentation on how to [setup iOS with Firebase Cloud Messaging](/messaging/usage/ios-setup).
|
||||
|
||||
If you're using an older version of React Native without auto-linking support, or wish to integrate into an existing project,
|
||||
you can follow the manual installation steps for [iOS](/messaging/usage/installation/ios) and [Android](/messaging/usage/installation/android).
|
||||
@@ -40,31 +40,13 @@ The module also provides basic support for displaying local notifications, to le
|
||||
|
||||
# Usage
|
||||
|
||||
Before receiving messages from the FCM service, you must first register your device and to use the service on iOS, you need to request the explicit users
|
||||
permission to accept incoming messages.
|
||||
|
||||
## Registering devices with FCM
|
||||
|
||||
Before devices can receive and send messages via FCM, they must be registered. The library exposes a `registerDeviceForRemoteMessages`
|
||||
method which should be called early on in your apps life-cycle. It is recommended that this method is called on every app
|
||||
boot:
|
||||
|
||||
```js
|
||||
import messaging from '@react-native-firebase/messaging';
|
||||
|
||||
async function registerAppWithFCM() {
|
||||
await messaging().registerDeviceForRemoteMessages();
|
||||
}
|
||||
```
|
||||
|
||||
## iOS - Requesting permissions
|
||||
|
||||
iOS prevents messages from being delivered to devices unless you have received explicit permission from the user. This includes
|
||||
messages which include data payloads and/or notification payloads.
|
||||
iOS prevents messages containing notification (or 'alert') payloads from being displayed unless you have received explicit permission from the user.
|
||||
|
||||
> To learn more about local notifications, view the [Notifications](/messaging/notifications) documentation.
|
||||
|
||||
The module provides a `requestPermission` method which triggers a native permission dialog requesting the user's permission:
|
||||
This module provides a `requestPermission` method which triggers a native permission dialog requesting the user's permission:
|
||||
|
||||
```js
|
||||
import messaging from '@react-native-firebase/messaging';
|
||||
@@ -306,6 +288,32 @@ messaging()
|
||||
|
||||
Messaging can be further configured to provide more control over how FCM is handled internally within your application.
|
||||
|
||||
## Auto Registration (iOS)
|
||||
|
||||
React Native Firebase Messaging automatically registers the device with APNs to receive remote messages. If you need
|
||||
to manually control registration you can disable this via the `firebase.json` file:
|
||||
|
||||
```json
|
||||
// <projectRoot>/firebase.json
|
||||
{
|
||||
"react-native": {
|
||||
"messaging_ios_auto_register_for_remote_messages": false,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once auto-registration is disabled you must manually call `registerDeviceForRemoteMessages` in your JavaScript code as
|
||||
early as possible in your application startup;
|
||||
|
||||
```js
|
||||
import messaging from '@react-native-firebase/messaging';
|
||||
|
||||
async function registerAppWithFCM() {
|
||||
await messaging().registerDeviceForRemoteMessages();
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Auto initialization
|
||||
|
||||
Firebase generates an Instance ID, which FCM uses to generate a registration token and which Analytics uses for data collection.
|
||||
|
||||
@@ -8,8 +8,8 @@ There are also a number of prerequisites which are required to be able to enable
|
||||
|
||||
- You must have an active [Apple Developer Account](https://developer.apple.com/membercenter/index.action).
|
||||
- You must have a physical iOS device to receive messages.
|
||||
- Firebase Cloud Messaging integrates with the [Apple Push Notification Service (APNS)](https://developer.apple.com/notifications/),
|
||||
however APNS only works with real devices.
|
||||
- Firebase Cloud Messaging integrates with the [Apple Push Notification service (APNs)](https://developer.apple.com/notifications/),
|
||||
however APNs only works with real devices.
|
||||
|
||||
# Configuring your app
|
||||
|
||||
@@ -55,10 +55,10 @@ Now ensure that both the "Background fetch" and the "Remote notifications" sub-m
|
||||
|
||||

|
||||
|
||||
# Linking APNS with FCM
|
||||
# Linking APNs with FCM
|
||||
|
||||
Even though FCM a has limited capability to work without linking with APNS, the below steps are strongly recommended
|
||||
to ensure the library works as expected. Without linking APNS, your device will not receive messages when in the background
|
||||
Even though FCM a has limited capability to work without linking with APNs, the below steps are strongly recommended
|
||||
to ensure the library works as expected. Without linking APNs, your device will not receive messages when in the background
|
||||
or when quit.
|
||||
|
||||
A few steps are required:
|
||||
@@ -75,11 +75,11 @@ tab on the account sidebar:
|
||||
|
||||
## 1. Registering a key
|
||||
|
||||
A key can be generated which gives the FCM full access over the Apple Push Notification (APNS) service. On the "Keys" menu item,
|
||||
register a new key. The name of the key can be anything, however you must ensure the "Apple Push Notification (APNS)" service
|
||||
A key can be generated which gives the FCM full access over the Apple Push Notification service (APNs). On the "Keys" menu item,
|
||||
register a new key. The name of the key can be anything, however you must ensure the APNs service
|
||||
is enabled:
|
||||
|
||||

|
||||

|
||||
|
||||
Click "Continue" & then "Save". Once saved, you will be presented with a screen displaying the private "Key ID" & the ability
|
||||
to download the key. Copy the ID, and download the file to your local machine:
|
||||
|
||||
@@ -67,8 +67,8 @@ date: 2020-04-03
|
||||
- This fixes an issue where FCM would throw a `"The operation couldn’t be completed"` error ([`#2657`](https://github.com/invertase/react-native-firebase/issues/2657))
|
||||
- `iOS`: direct FCM connection is now fixed.
|
||||
- When the app was in the foreground, `data-only` messages were not coming through, they are now.
|
||||
- `iOS`: when running debug build, the APNS token will be registered with FCM as a `"sandbox"` key type
|
||||
- `iOS`: the original APNS swizzling we implemented was not functioning correctly with `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
|
||||
- `iOS`: when running debug build, the APNs token will be registered with FCM as a `"sandbox"` key type
|
||||
- `iOS`: the original APNs swizzling we implemented was not functioning correctly with `application:didReceiveRemoteNotification:fetchCompletionHandler:`.
|
||||
- We added additional logic whereby this is executed in all scenarios (foreground/background/quit) and replaces a deprecated Apple API.
|
||||
- This fixes issues with `data-only` messages not being handled by the device
|
||||
- `iOS`: any custom `FIRMessagingDelegate` methods you add to your `AppDelegate.m` will now also be called internally by React Native Firebase messaging.
|
||||
|
||||
9909
docs/typedoc.json
vendored
9909
docs/typedoc.json
vendored
File diff suppressed because it is too large
Load Diff
2
docs/typedoc.min.json
vendored
2
docs/typedoc.min.json
vendored
File diff suppressed because one or more lines are too long
@@ -83,7 +83,8 @@
|
||||
if (firebaseJsonRaw == nil) {
|
||||
return @"{}";
|
||||
}
|
||||
|
||||
return firebaseJsonRaw;
|
||||
|
||||
NSData *data = [[NSData alloc] initWithBase64EncodedString:firebaseJsonRaw options:0];
|
||||
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -19,6 +19,7 @@ package io.invertase.firebase.messaging;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
@@ -55,23 +56,29 @@ public class ReactNativeFirebaseMessagingModule extends ReactNativeFirebaseModul
|
||||
initialNotification = null;
|
||||
return;
|
||||
} else {
|
||||
Intent intent = getCurrentActivity().getIntent();
|
||||
Activity activity = getCurrentActivity();
|
||||
|
||||
if (intent != null && intent.getExtras() != null) {
|
||||
// messageId can be either one...
|
||||
String messageId = intent.getExtras().getString("google.message_id");
|
||||
if (messageId == null) messageId = intent.getExtras().getString("message_id");
|
||||
if (activity != null) {
|
||||
Intent intent = activity.getIntent();
|
||||
|
||||
// only handle non-consumed initial notifications
|
||||
if (messageId != null && initialNotificationMap.get(messageId) == null) {
|
||||
RemoteMessage remoteMessage = ReactNativeFirebaseMessagingReceiver.notifications.get(messageId);
|
||||
if (intent != null && intent.getExtras() != null) {
|
||||
// messageId can be either one...
|
||||
String messageId = intent.getExtras().getString("google.message_id");
|
||||
if (messageId == null) messageId = intent.getExtras().getString("message_id");
|
||||
|
||||
if (remoteMessage != null) {
|
||||
promise.resolve(ReactNativeFirebaseMessagingSerializer.remoteMessageToWritableMap(remoteMessage));
|
||||
initialNotificationMap.put(messageId, true);
|
||||
return;
|
||||
// only handle non-consumed initial notifications
|
||||
if (messageId != null && initialNotificationMap.get(messageId) == null) {
|
||||
RemoteMessage remoteMessage = ReactNativeFirebaseMessagingReceiver.notifications.get(messageId);
|
||||
|
||||
if (remoteMessage != null) {
|
||||
promise.resolve(ReactNativeFirebaseMessagingSerializer.remoteMessageToWritableMap(remoteMessage));
|
||||
initialNotificationMap.put(messageId, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, "Attempt to call getInitialNotification failed. The current activity is not ready, try calling the method later in the React lifecycle.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,13 @@
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
|
||||
#if __has_include(<FirebaseAuth/FirebaseAuth.h>)
|
||||
if ([[FIRAuth auth] canHandleNotification:userInfo]) {
|
||||
completionHandler(UIBackgroundFetchResultNoData);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (userInfo[@"gcm.message_id"]) {
|
||||
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
|
||||
// TODO add support in a later version for calling completion handler directly from JS when user JS code complete
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <RNFBApp/RNFBRCTEventEmitter.h>
|
||||
#import <RNFBApp/RNFBJSON.h>
|
||||
|
||||
#import "RNFBMessaging+AppDelegate.h"
|
||||
#import "RNFBMessaging+NSNotificationCenter.h"
|
||||
@@ -124,6 +125,12 @@
|
||||
) {
|
||||
rctRootView = (RCTRootView *) [UIApplication sharedApplication].delegate.window.rootViewController.view;
|
||||
}
|
||||
|
||||
#if !(TARGET_IPHONE_SIMULATOR)
|
||||
if ([[RNFBJSON shared] getBooleanValue:@"messaging_ios_auto_register_for_remote_messages" defaultValue:YES]) {
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (notification.userInfo[UIApplicationLaunchOptionsRemoteNotificationKey]) {
|
||||
if ([UIApplication sharedApplication].applicationState == UIApplicationStateBackground) {
|
||||
@@ -137,6 +144,8 @@
|
||||
// application:didReceiveRemoteNotification:fetchCompletionHandler: will not get called unless registerForRemoteNotifications
|
||||
// was called early during app initialization - we call it here in this scenario as the user can only call this via JS, at which point
|
||||
// it'd be too late resulting in the app being terminated.
|
||||
// called irregardless of `messaging_ios_auto_register_for_remote_messages` as this is most likely an app launching
|
||||
// as a result of a remote notification - so has been registered previously
|
||||
[[UIApplication sharedApplication] registerForRemoteNotifications];
|
||||
#endif
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface RNFBMessagingUNUserNotificationCenter : NSObject <UNUserNotificationCenterDelegate>
|
||||
|
||||
@property NSDictionary* initialNotification;
|
||||
@property NSDictionary *initialNotification;
|
||||
@property(nonatomic, nullable, weak) id <UNUserNotificationCenterDelegate> originalDelegate;
|
||||
|
||||
+ (_Nonnull instancetype)sharedInstance;
|
||||
|
||||
|
||||
@@ -21,6 +21,11 @@
|
||||
#import "RNFBMessaging+UNUserNotificationCenter.h"
|
||||
|
||||
@implementation RNFBMessagingUNUserNotificationCenter
|
||||
struct {
|
||||
unsigned int willPresentNotification:1;
|
||||
unsigned int didReceiveNotificationResponse:1;
|
||||
unsigned int openSettingsForNotification:1;
|
||||
} originalDelegateRespondsTo;
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static dispatch_once_t once;
|
||||
@@ -38,6 +43,12 @@
|
||||
dispatch_once(&once, ^{
|
||||
RNFBMessagingUNUserNotificationCenter *strongSelf = weakSelf;
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
if (center.delegate != nil) {
|
||||
_originalDelegate = center.delegate;
|
||||
originalDelegateRespondsTo.openSettingsForNotification = (unsigned int) [_originalDelegate respondsToSelector:@selector(userNotificationCenter:openSettingsForNotification:)];
|
||||
originalDelegateRespondsTo.willPresentNotification = (unsigned int) [_originalDelegate respondsToSelector:@selector(userNotificationCenter:willPresentNotification:withCompletionHandler:)];
|
||||
originalDelegateRespondsTo.didReceiveNotificationResponse = (unsigned int) [_originalDelegate respondsToSelector:@selector(userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:)];
|
||||
}
|
||||
center.delegate = strongSelf;
|
||||
});
|
||||
}
|
||||
@@ -64,6 +75,10 @@
|
||||
|
||||
// TODO in a later version allow customising completion options in JS code
|
||||
completionHandler(UNNotificationPresentationOptionNone);
|
||||
} else if (_originalDelegate != nil && originalDelegateRespondsTo.willPresentNotification) {
|
||||
[_originalDelegate userNotificationCenter:center willPresentNotification:notification withCompletionHandler:completionHandler];
|
||||
} else {
|
||||
completionHandler(UNNotificationPresentationOptionNone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +89,16 @@
|
||||
[[RNFBRCTEventEmitter shared] sendEventWithName:@"messaging_notification_opened" body:notificationDict];
|
||||
_initialNotification = notificationDict;
|
||||
completionHandler();
|
||||
} else if (_originalDelegate != nil && originalDelegateRespondsTo.didReceiveNotificationResponse) {
|
||||
[_originalDelegate userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:completionHandler];
|
||||
} else {
|
||||
completionHandler();
|
||||
}
|
||||
}
|
||||
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification {
|
||||
if (_originalDelegate != nil && originalDelegateRespondsTo.openSettingsForNotification) {
|
||||
[_originalDelegate userNotificationCenter:center openSettingsForNotification:notification];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
22
packages/messaging/lib/index.d.ts
vendored
22
packages/messaging/lib/index.d.ts
vendored
@@ -108,14 +108,14 @@ export namespace FirebaseMessagingTypes {
|
||||
notification?: Notification;
|
||||
|
||||
/**
|
||||
* Whether the iOS APNS message was configured as a background update notification.
|
||||
* Whether the iOS APNs message was configured as a background update notification.
|
||||
*
|
||||
* @platform ios iOS
|
||||
*/
|
||||
contentAvailable?: boolean;
|
||||
|
||||
/**
|
||||
* Whether the iOS APNS `mutable-content` property on the message was set
|
||||
* Whether the iOS APNs `mutable-content` property on the message was set
|
||||
* allowing the app to modify the notification via app extensions.
|
||||
*
|
||||
* @platform ios iOS
|
||||
@@ -678,9 +678,8 @@ export namespace FirebaseMessagingTypes {
|
||||
registerForRemoteNotifications(): Promise<void>;
|
||||
|
||||
/**
|
||||
* On iOS, if your app wants to receive remote messages from FCM (via APNS), you must explicitly register
|
||||
* this request with APNS. For example if you want to display alerts, play sounds
|
||||
* or perform other user-facing actions (via the Notification library), you must call this method.
|
||||
* On iOS, if your app wants to receive remote messages from FCM (via APNs), you must explicitly register
|
||||
* with APNs if auto-registration has been disabled.
|
||||
*
|
||||
* > You can safely call this method on Android without platform checks. It's a no-op on Android and will promise resolve `void`.
|
||||
*
|
||||
@@ -746,7 +745,7 @@ export namespace FirebaseMessagingTypes {
|
||||
unregisterDeviceForRemoteMessages(): Promise<void>;
|
||||
|
||||
/**
|
||||
* On iOS, it is possible to get the users APNS token. This may be required if you want to send messages to your
|
||||
* On iOS, it is possible to get the users APNs token. This may be required if you want to send messages to your
|
||||
* iOS devices without using the FCM service.
|
||||
*
|
||||
* > You can safely call this method on Android without platform checks. It's a no-op on Android and will promise resolve `null`.
|
||||
@@ -757,7 +756,7 @@ export namespace FirebaseMessagingTypes {
|
||||
* const apnsToken = await firebase.messaging().getAPNSToken();
|
||||
*
|
||||
* if (apnsToken) {
|
||||
* console.log('User APNS Token:', apnsToken);
|
||||
* console.log('User APNs Token:', apnsToken);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
@@ -967,5 +966,14 @@ namespace ReactNativeFirebase {
|
||||
messaging_android_headless_task_timeout?: number;
|
||||
messaging_android_notification_channel_id?: string;
|
||||
messaging_android_notification_color?: string;
|
||||
/**
|
||||
* Whether RNFirebase Messaging automatically calls `[[UIApplication sharedApplication] registerForRemoteNotifications];`
|
||||
* automatically on app launch (recommended) - defaults to true.
|
||||
*
|
||||
* If set to false; make sure to call `firebase.messaging().registerDeviceForRemoteMessages()`
|
||||
* early on in your app startup - otherwise you will NOT receive remote messages/notifications
|
||||
* in your app.
|
||||
*/
|
||||
messaging_ios_auto_register_for_remote_messages?: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +264,15 @@ class FirebaseMessagingModule extends FirebaseModule {
|
||||
if (isAndroid) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const autoRegister = this.firebaseJson['messaging_ios_auto_register_for_remote_messages'];
|
||||
if (autoRegister === undefined || autoRegister === true) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`Usage of "messaging().registerDeviceForRemoteMessages()" is not required. You only need to register if auto-registration is disabled in your 'firebase.json' configuration file via the 'messaging_ios_auto_register_for_remote_messages' property.`,
|
||||
);
|
||||
}
|
||||
|
||||
this._isRegisteredForRemoteNotifications = true;
|
||||
return this.native.registerForRemoteNotifications();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
"messaging_android_headless_task_timeout": 30000,
|
||||
"messaging_android_notification_channel_id": "",
|
||||
"messaging_android_notification_color": "@color/hotpink",
|
||||
"messaging_ios_auto_register_for_remote_messages": true,
|
||||
|
||||
"ml_vision_label_model": true,
|
||||
"ml_vision_image_label_model": true,
|
||||
|
||||
Reference in New Issue
Block a user