Commit eb9b29fe authored by Libin Lu's avatar Libin Lu

handle iOS completion handler

parent c0aa9877
import {NativeModules, DeviceEventEmitter, Platform} from 'react-native'; import {NativeModules, DeviceEventEmitter, Platform} from 'react-native';
const eventsMap = { export const FCMEvent = {
refreshToken: 'FCMTokenRefreshed', RefreshToken: 'FCMTokenRefreshed',
notification: 'FCMNotificationReceived' Notification: 'FCMNotificationReceived'
}; }
export const RemoteNotificationResult = {
NewData: 'UIBackgroundFetchResultNewData',
NoData: 'UIBackgroundFetchResultNoData',
ResultFailed: 'UIBackgroundFetchResultFailed'
}
export const WillPresentNotificationResult = {
All: 'UNNotificationPresentationOptionAll',
None: 'UNNotificationPresentationOptionNone'
}
const RNFIRMessaging = NativeModules.RNFIRMessaging; const RNFIRMessaging = NativeModules.RNFIRMessaging;
...@@ -69,13 +80,57 @@ FCM.getBadgeNumber = () => { ...@@ -69,13 +80,57 @@ FCM.getBadgeNumber = () => {
return RNFIRMessaging.getBadgeNumber(); return RNFIRMessaging.getBadgeNumber();
} }
function finish(result){
if(Platform.OS !== 'ios'){
return;
}
if(!this._finishCalled && this._completionHandlerId){
this._finishCalled = true;
switch(this.notification_event_type){
case 'remote_notification':
result = result || RemoteNotificationResult.NoData;
if(!Object.values(RemoteNotificationResult).includes(result)){
throw new Error(`Invalid RemoteNotificationResult, use import {RemoteNotificationResult} from 'react-native-fcm' to avoid typo`);
}
RNFIRMessaging.finishRemoteNotification(this._completionHandlerId, result);
return;
case 'notification_response':
RNFIRMessaging.finishNotificationResponse(this._completionHandlerId);
return;
case 'will_present_notification':
result = result || (this.show_in_foreground ? WillPresentNotificationResult.All : WillPresentNotificationResult.None);
if(!Object.values(WillPresentNotificationResult).includes(result)){
throw new Error(`Invalid WillPresentNotificationResult, make sure you use import {WillPresentNotificationResult} from 'react-native-fcm' to avoid typo`);
}
RNFIRMessaging.finishWillPresentNotification(this._completionHandlerId, result);
return;
default:
return;
}
}
}
FCM.on = (event, callback) => { FCM.on = (event, callback) => {
const nativeEvent = eventsMap[event]; if (!Object.values(FCMEvent).includes(event)) {
if (!nativeEvent) { throw new Error(`Invalid FCM event subscription, use import {FCMEvent} from 'react-native-fcm' to avoid typo`);
throw new Error('FCM event must be "refreshToken" or "notification"');
}; };
return DeviceEventEmitter.addListener(nativeEvent, callback); if(event === FCMEvent.Notification){
return DeviceEventEmitter.addListener(event, async(data)=>{
data.finish = finish;
try{
await callback();
} catch(err){
console.error('Notification handler err', err)
throw err;
}
if(!data._finishCalled){
data.finish();
}
})
}
return DeviceEventEmitter.addListener(event, callback);
}; };
FCM.subscribeToTopic = (topic) => { FCM.subscribeToTopic = (topic) => {
...@@ -90,4 +145,4 @@ FCM.send = (senderId, payload) => { ...@@ -90,4 +145,4 @@ FCM.send = (senderId, payload) => {
RNFIRMessaging.send(senderId, payload); RNFIRMessaging.send(senderId, payload);
}; };
module.exports = FCM; export default FCM;
...@@ -5,12 +5,18 @@ ...@@ -5,12 +5,18 @@
#import <React/RCTBridgeModule.h> #import <React/RCTBridgeModule.h>
@import UserNotifications;
extern NSString *const FCMNotificationReceived;
@interface RNFIRMessaging : NSObject <RCTBridgeModule> @interface RNFIRMessaging : NSObject <RCTBridgeModule>
@property (nonatomic, assign) bool connectedToFCM; @property (nonatomic, assign) bool connectedToFCM;
#if !TARGET_OS_TV
+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler;
+ (void)didReceiveLocalNotification:(nonnull UILocalNotification *)notification;
+ (void)didReceiveNotificationResponse:(nonnull UNNotificationResponse *)response withCompletionHandler:(nonnull void (^)())completionHandler;
+ (void)willPresentNotification:(nonnull UNNotification *)notification withCompletionHandler:(nonnull void (^)(UNNotificationPresentationOptions))completionHandler;
#endif
@end @end
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
#endif #endif
typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result);
typedef void (^RCTWillPresentNotificationCallback)(UNNotificationPresentationOptions result);
typedef void (^RCTNotificationResponseCallback)();
NSString *const FCMNotificationReceived = @"FCMNotificationReceived"; NSString *const FCMNotificationReceived = @"FCMNotificationReceived";
@implementation RCTConvert (NSCalendarUnit) @implementation RCTConvert (NSCalendarUnit)
...@@ -115,6 +119,21 @@ RCT_ENUM_CONVERTER(NSCalendarUnit, ...@@ -115,6 +119,21 @@ RCT_ENUM_CONVERTER(NSCalendarUnit,
notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"badge"]]; notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"badge"]];
return notification; return notification;
} }
RCT_ENUM_CONVERTER(UIBackgroundFetchResult, (@{
@"UIBackgroundFetchResultNewData": @(UIBackgroundFetchResultNewData),
@"UIBackgroundFetchResultNoData": @(UIBackgroundFetchResultNoData),
@"UIBackgroundFetchResultFailed": @(UIBackgroundFetchResultFailed),
}), UIBackgroundFetchResultNoData, integerValue)
RCT_ENUM_CONVERTER(UNNotificationPresentationOptions, (@{
@"UNNotificationPresentationOptionAll": @(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound),
@"UNNotificationPresentationOptionNone": @(UNNotificationPresentationOptionNone)}), UIBackgroundFetchResultNoData, integerValue)
@end
@interface RNFIRMessaging ()
@property (nonatomic, strong) NSMutableDictionary *notificationCallbacks;
@end @end
@implementation RNFIRMessaging @implementation RNFIRMessaging
...@@ -123,6 +142,34 @@ RCT_EXPORT_MODULE() ...@@ -123,6 +142,34 @@ RCT_EXPORT_MODULE()
@synthesize bridge = _bridge; @synthesize bridge = _bridge;
+ (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler {
NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: userInfo];
[data setValue:@"remote_notification" forKey:@"notification_event_type"];
[data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"];
[[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}];
}
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification {
NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: notification.userInfo];
[data setValue:@"local_notification" forKey:@"notification_event_type"];
[[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:data];
}
+ (void)didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(RCTNotificationResponseCallback)completionHandler
{
NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: response.notification.request.content.userInfo];
[data setValue:@"notification_response" forKey:@"notification_event_type"];
[data setValue:@YES forKey:@"opened_from_tray"];
[[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}];
}
+ (void)willPresentNotification:(UNNotification *)notification withCompletionHandler:(RCTWillPresentNotificationCallback)completionHandler
{
NSMutableDictionary* data = [[NSMutableDictionary alloc] initWithDictionary: notification.request.content.userInfo];
[data setValue:@"will_present_notification" forKey:@"notification_event_type"];
[[NSNotificationCenter defaultCenter] postNotificationName:FCMNotificationReceived object:self userInfo:@{@"data": data, @"completionHandler": completionHandler}];
}
- (void)dealloc - (void)dealloc
{ {
[[NSNotificationCenter defaultCenter] removeObserver:self]; [[NSNotificationCenter defaultCenter] removeObserver:self];
...@@ -377,16 +424,52 @@ RCT_EXPORT_METHOD(send:(NSString*)senderId withPayload:(NSDictionary *)message) ...@@ -377,16 +424,52 @@ RCT_EXPORT_METHOD(send:(NSString*)senderId withPayload:(NSDictionary *)message)
[[FIRMessaging messaging]sendMessage:imMessage to:receiver withMessageID:messageID timeToLive:ttl]; [[FIRMessaging messaging]sendMessage:imMessage to:receiver withMessageID:messageID timeToLive:ttl];
} }
RCT_EXPORT_METHOD(finishRemoteNotification: (NSString *)completionHandlerId fetchResult:(UIBackgroundFetchResult)result){
RCTRemoteNotificationCallback completionHandler = self.notificationCallbacks[completionHandlerId];
if (!completionHandler) {
RCTLogError(@"There is no completion handler with completionHandlerId: %@", completionHandlerId);
return;
}
completionHandler(result);
[self.notificationCallbacks removeObjectForKey:completionHandlerId];
}
RCT_EXPORT_METHOD(finishWillPresentNotification: (NSString *)completionHandlerId fetchResult:(UNNotificationPresentationOptions)result){
RCTWillPresentNotificationCallback completionHandler = self.notificationCallbacks[completionHandlerId];
if (!completionHandler) {
RCTLogError(@"There is no completion handler with completionHandlerId: %@", completionHandlerId);
return;
}
completionHandler(result);
[self.notificationCallbacks removeObjectForKey:completionHandlerId];
}
RCT_EXPORT_METHOD(finishNotificationResponse: (NSString *)completionHandlerId){
RCTNotificationResponseCallback completionHandler = self.notificationCallbacks[completionHandlerId];
if (!completionHandler) {
RCTLogError(@"There is no completion handler with completionHandlerId: %@", completionHandlerId);
return;
}
completionHandler();
[self.notificationCallbacks removeObjectForKey:completionHandlerId];
}
- (void)handleNotificationReceived:(NSNotification *)notification - (void)handleNotificationReceived:(NSNotification *)notification
{ {
if([notification.userInfo valueForKey:@"opened_from_tray"] == nil){ id completionHandler = notification.userInfo[@"completionHandler"];
NSMutableDictionary *data = [[NSMutableDictionary alloc]initWithDictionary: notification.userInfo]; NSMutableDictionary* data = notification.userInfo[@"data"];
[data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"]; if(completionHandler != nil){
[_bridge.eventDispatcher sendDeviceEventWithName:FCMNotificationReceived body:data]; NSString *completionHandlerId = [[NSUUID UUID] UUIDString];
}else{ if (!self.notificationCallbacks) {
[_bridge.eventDispatcher sendDeviceEventWithName:FCMNotificationReceived body:notification.userInfo]; // Lazy initialization
self.notificationCallbacks = [NSMutableDictionary dictionary];
}
self.notificationCallbacks[completionHandlerId] = completionHandler;
data[@"_completionHandlerId"] = completionHandlerId;
} }
[_bridge.eventDispatcher sendDeviceEventWithName:FCMNotificationReceived body:data];
} }
- (void)sendDataMessageFailure:(NSNotification *)notification - (void)sendDataMessageFailure:(NSNotification *)notification
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment