Only store completionHandlers on iOS native side when user adds listener

Note the following cases. This commit is catering for case 3:
1. User is listening for onNotificationDisplayed and is manually calling
the completionHandler
- manually called completionHandler is removed
- automatically called completionHandler is guarded against on iOS
native side

2. User is listening for onNotificationDisplayed and is not calling the
completionHandler:
- automatically called completionHandler is removed

3. User is not listening for onNotificationDisplayed
- On rn side we can only automatically call completionHandler if the
user _is_ listening. This means we need to detect if the user is
listening or not.
This commit is contained in:
Ryan Grey 2018-07-27 11:42:22 +01:00
parent 0d14a5e3b3
commit 5168f96a32
2 changed files with 49 additions and 7 deletions

View File

@ -17,6 +17,7 @@
@end
@implementation RNFirebaseNotifications {
BOOL isUserHandlingOnNotificationDisplayed;
NSMutableDictionary<NSString *, void (^)(UIBackgroundFetchResult)> *completionHandlers;
}
@ -56,8 +57,6 @@ RCT_EXPORT_MODULE();
// Set static instance for use from AppDelegate
theRNFirebaseNotifications = self;
completionHandlers = [[NSMutableDictionary alloc] init];
}
// PRE-BRIDGE-EVENTS: Consider enabling this to allow events built up before the bridge is built to be sent to the JS side
@ -99,6 +98,21 @@ RCT_EXPORT_MODULE();
}
}
RCT_EXPORT_METHOD(startHandlingNotificationDisplayed) {
isUserHandlingOnNotificationDisplayed = YES;
completionHandlers = [[NSMutableDictionary alloc] init];
}
RCT_EXPORT_METHOD(stopHandlingNotificationDisplayed) {
isUserHandlingOnNotificationDisplayed = NO;
NSArray *allHandlers = completionHandlers.allValues;
for (void (^ completionHandler)(UIBackgroundFetchResult) in allHandlers) {
completionHandler(UIBackgroundFetchResultNoData);
}
[completionHandlers removeAllObjects];
completionHandlers = nil;
}
RCT_EXPORT_METHOD(complete:(NSString*)handlerKey fetchResult:(NSString *)rnFetchResult) {
UIBackgroundFetchResult fetchResult = UIBackgroundFetchResultNoData;
if ([@"noData" isEqualToString:rnFetchResult]) {
@ -117,6 +131,15 @@ RCT_EXPORT_METHOD(complete:(NSString*)handlerKey fetchResult:(NSString *)rnFetch
}
}
- (void)executeOrStoreCompletionHandler:(void (^ _Nonnull)(UIBackgroundFetchResult))completionHandler notification:(NSDictionary *)notification {
if(isUserHandlingOnNotificationDisplayed) {
NSString *handlerKey = notification[@"notificationId"];
completionHandlers[handlerKey] = completionHandler;
} else {
completionHandler(UIBackgroundFetchResultNoData);
}
}
// Listen for background messages
- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
@ -124,9 +147,12 @@ RCT_EXPORT_METHOD(complete:(NSString*)handlerKey fetchResult:(NSString *)rnFetch
// Pass them over to the RNFirebaseMessaging handler instead
if (userInfo[@"aps"] && ((NSDictionary*)userInfo[@"aps"]).count == 1 && userInfo[@"aps"][@"content-available"]) {
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo];
completionHandler(UIBackgroundFetchResultNoData);
return;
}
NSDictionary *notification = [self parseUserInfo:userInfo];
NSString *event;
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) {
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
@ -141,10 +167,10 @@ RCT_EXPORT_METHOD(complete:(NSString*)handlerKey fetchResult:(NSString *)rnFetch
// - foreground notifications also go through willPresentNotification
// - background notification presses also go through didReceiveNotificationResponse
// This prevents duplicate messages from hitting the JS app
completionHandler(UIBackgroundFetchResultNoData);
return;
}
NSDictionary *notification = [self parseUserInfo:userInfo];
// For onOpened events, we set the default action name as iOS 8/9 has no concept of actions
if (event == NOTIFICATIONS_NOTIFICATION_OPENED) {
notification = @{
@ -153,8 +179,7 @@ RCT_EXPORT_METHOD(complete:(NSString*)handlerKey fetchResult:(NSString *)rnFetch
};
}
NSString *handlerKey = notification[@"notificationId"];
completionHandlers[handlerKey] = completionHandler;
[self executeOrStoreCompletionHandler:completionHandler notification:notification];
[self sendJSEvent:self name:event body:notification];
}

View File

@ -54,6 +54,11 @@ const NATIVE_EVENTS = [
export const MODULE_NAME = 'RNFirebaseNotifications';
export const NAMESPACE = 'notifications';
const hasListeners = (eventType: string): boolean => {
const listeners = SharedEventEmitter.listeners(eventType);
return listeners && listeners.length;
};
// iOS 8/9 scheduling
// fireDate: Date;
// timeZone: TimeZone;
@ -255,11 +260,23 @@ export default class Notifications extends ModuleBase {
}
getLogger(this).info('Creating onNotificationDisplayed listener');
SharedEventEmitter.addListener('onNotificationDisplayed', listener);
SharedEventEmitter.addListener(
'onNotificationDisplayed',
listener
);
if (hasListeners('onNotificationDisplayed')) {
getNativeModule(this).startHandlingNotificationDisplayed();
}
return () => {
getLogger(this).info('Removing onNotificationDisplayed listener');
SharedEventEmitter.removeListener('onNotificationDisplayed', listener);
SharedEventEmitter.removeListener(
'onNotificationDisplayed',
listener
);
if (!hasListeners('onNotificationDisplayed')) {
getNativeModule(this).stopHandlingNotificationDisplayed();
}
};
}