Commit eb9b29fe authored by Libin Lu's avatar Libin Lu

handle iOS completion handler

parent c0aa9877
import {NativeModules, DeviceEventEmitter, Platform} from 'react-native';
const eventsMap = {
refreshToken: 'FCMTokenRefreshed',
notification: 'FCMNotificationReceived'
};
export const FCMEvent = {
RefreshToken: 'FCMTokenRefreshed',
Notification: 'FCMNotificationReceived'
}
export const RemoteNotificationResult = {
NewData: 'UIBackgroundFetchResultNewData',
NoData: 'UIBackgroundFetchResultNoData',
ResultFailed: 'UIBackgroundFetchResultFailed'
}
export const WillPresentNotificationResult = {
All: 'UNNotificationPresentationOptionAll',
None: 'UNNotificationPresentationOptionNone'
}
const RNFIRMessaging = NativeModules.RNFIRMessaging;
......@@ -69,13 +80,57 @@ FCM.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) => {
const nativeEvent = eventsMap[event];
if (!nativeEvent) {
throw new Error('FCM event must be "refreshToken" or "notification"');
if (!Object.values(FCMEvent).includes(event)) {
throw new Error(`Invalid FCM event subscription, use import {FCMEvent} from 'react-native-fcm' to avoid typo`);
};
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) => {
......@@ -90,4 +145,4 @@ FCM.send = (senderId, payload) => {
RNFIRMessaging.send(senderId, payload);
};
module.exports = FCM;
export default FCM;
......@@ -5,12 +5,18 @@
#import <React/RCTBridgeModule.h>
extern NSString *const FCMNotificationReceived;
@import UserNotifications;
@interface RNFIRMessaging : NSObject <RCTBridgeModule>
@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
......@@ -18,6 +18,10 @@
#endif
typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result);
typedef void (^RCTWillPresentNotificationCallback)(UNNotificationPresentationOptions result);
typedef void (^RCTNotificationResponseCallback)();
NSString *const FCMNotificationReceived = @"FCMNotificationReceived";
@implementation RCTConvert (NSCalendarUnit)
......@@ -115,6 +119,21 @@ RCT_ENUM_CONVERTER(NSCalendarUnit,
notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"badge"]];
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
@implementation RNFIRMessaging
......@@ -123,6 +142,34 @@ RCT_EXPORT_MODULE()
@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
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
......@@ -377,16 +424,52 @@ RCT_EXPORT_METHOD(send:(NSString*)senderId withPayload:(NSDictionary *)message)
[[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
{
if([notification.userInfo valueForKey:@"opened_from_tray"] == nil){
NSMutableDictionary *data = [[NSMutableDictionary alloc]initWithDictionary: notification.userInfo];
[data setValue:@(RCTSharedApplication().applicationState == UIApplicationStateInactive) forKey:@"opened_from_tray"];
[_bridge.eventDispatcher sendDeviceEventWithName:FCMNotificationReceived body:data];
}else{
[_bridge.eventDispatcher sendDeviceEventWithName:FCMNotificationReceived body:notification.userInfo];
id completionHandler = notification.userInfo[@"completionHandler"];
NSMutableDictionary* data = notification.userInfo[@"data"];
if(completionHandler != nil){
NSString *completionHandlerId = [[NSUUID UUID] UUIDString];
if (!self.notificationCallbacks) {
// Lazy initialization
self.notificationCallbacks = [NSMutableDictionary dictionary];
}
self.notificationCallbacks[completionHandlerId] = completionHandler;
data[@"_completionHandlerId"] = completionHandlerId;
}
[_bridge.eventDispatcher sendDeviceEventWithName:FCMNotificationReceived body:data];
}
- (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