diff --git a/native/ios/Comm/CommIOSNotifications/CommIOSNotifications.mm b/native/ios/Comm/CommIOSNotifications/CommIOSNotifications.mm --- a/native/ios/Comm/CommIOSNotifications/CommIOSNotifications.mm +++ b/native/ios/Comm/CommIOSNotifications/CommIOSNotifications.mm @@ -84,6 +84,124 @@ localNotification ? localNotification.userInfo : nil; } +/* + Public methods +*/ ++ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken { + NSString *tokenRepresentation = [deviceToken isKindOfClass:[NSString class]] + ? deviceToken + : [self deviceTokenToString:deviceToken]; + [[NSNotificationCenter defaultCenter] + postNotificationName:CommIOSNotificationsRegistered + object:self + userInfo:@{@"deviceToken" : tokenRepresentation}]; +} + ++ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + [[NSNotificationCenter defaultCenter] + postNotificationName:CommIOSNotificationsRegistrationFailed + object:self + userInfo:@{ + @"code" : [NSNumber numberWithInteger:error.code], + @"domain" : error.domain, + @"localizedDescription" : error.localizedDescription + }]; +} + ++ (void)didReceiveRemoteNotification:(NSDictionary *)notification + fetchCompletionHandler: + (void (^)(UIBackgroundFetchResult))completionHandler { + NSDictionary *notifInfo = @{ + @"notification" : notification, + @"completionHandler" : completionHandler + }; + UIApplicationState state = [UIApplication sharedApplication].applicationState; + + if ([CommIOSNotificationsBridgeQueue sharedInstance].jsIsReady == YES) { + // JS thread is ready, push the notification to the bridge + + if (state == UIApplicationStateActive) { + // Notification received foreground + [self didReceiveNotificationOnForegroundState:notifInfo]; + } else if (state == UIApplicationStateInactive) { + // Notification opened + [self didNotificationOpen:notifInfo]; + } else { + // Notification received background + [self didReceiveNotificationOnBackgroundState:notifInfo]; + } + } else { + // JS thread is not ready - store it in the native notifications queue + [[CommIOSNotificationsBridgeQueue sharedInstance] + postNotification:notifInfo]; + } +} + ++ (void)didReceiveLocalNotification:(UILocalNotification *)notification { + UIApplicationState state = [UIApplication sharedApplication].applicationState; + + NSMutableDictionary *newUserInfo = notification.userInfo.mutableCopy; + [newUserInfo removeObjectForKey:@"__id"]; + notification.userInfo = newUserInfo; + + NSDictionary *notifInfo = @{@"notification" : notification.userInfo}; + if (state == UIApplicationStateActive) { + [self didReceiveNotificationOnForegroundState:notifInfo]; + } else if (state == UIApplicationStateInactive) { + NSString *notificationId = + [notification.userInfo objectForKey:@"notificationId"]; + if (notificationId) { + [self clearNotificationFromNotificationsCenter:notificationId + completionHandler:nil]; + } + [self didNotificationOpen:notifInfo]; + } +} + ++ (void)clearNotificationFromNotificationsCenter:(NSString *)notificationId + completionHandler: + (void (^)(UIBackgroundFetchResult)) + completionHandler { + if ([UNUserNotificationCenter class]) { + [[UNUserNotificationCenter currentNotificationCenter] + getDeliveredNotificationsWithCompletionHandler:^( + NSArray *_Nonnull notifications) { + for (UNNotification *notif in notifications) { + if ([notificationId + isEqual:notif.request.content.userInfo[@"id"]]) { + NSArray *identifiers = + [NSArray arrayWithObjects:notif.request.identifier, nil]; + [[UNUserNotificationCenter currentNotificationCenter] + removeDeliveredNotificationsWithIdentifiers:identifiers]; + } + } + if (completionHandler) { + dispatch_async(dispatch_get_main_queue(), ^{ + completionHandler(UIBackgroundFetchResultNewData); + }); + } + }]; + return; + } + + NSString *notificationKey = + [self buildNotificationKeyfromNotification:notificationId]; + NSData *data = + [[NSUserDefaults standardUserDefaults] objectForKey:notificationKey]; + if (data) { + UILocalNotification *notification = + [NSKeyedUnarchiver unarchiveObjectWithData:data]; + + // delete the notification + [[UIApplication sharedApplication] cancelLocalNotification:notification]; + [[NSUserDefaults standardUserDefaults] removeObjectForKey:notificationKey]; + + NSLog(@"Local notification removed: %@", notificationKey); + + return; + } +} + /* JavaScript Events */ @@ -133,4 +251,93 @@ body:notification.userInfo]; } +/* + Notifications Handlers +*/ ++ (void)didReceiveNotificationOnForegroundState:(NSDictionary *)notifInfo { + [[NSNotificationCenter defaultCenter] + postNotificationName:CommIOSNotificationsReceivedForeground + object:self + userInfo:notifInfo]; +} + ++ (void)didReceiveNotificationOnBackgroundState:(NSDictionary *)notifInfo { + NSDictionary *notification = notifInfo[@"notification"]; + + NSDictionary *managedAps = [notification objectForKey:@"managedAps"]; + NSDictionary *alert = [managedAps objectForKey:@"alert"]; + NSString *action = [managedAps objectForKey:@"action"]; + NSString *notificationId = [managedAps objectForKey:@"notificationId"]; + + if (action && + ([action isEqualToString:CommIOSNotificationsCreateAction] && + notificationId && alert)) { + // create notification + [self dispatchLocalNotificationFromNotification:notification]; + } + + [[NSNotificationCenter defaultCenter] + postNotificationName:CommIOSNotificationsReceivedBackground + object:self + userInfo:notifInfo]; +} + ++ (void)didNotificationOpen:(NSDictionary *)notifInfo { + [[NSNotificationCenter defaultCenter] + postNotificationName:CommIOSNotificationsOpened + object:self + userInfo:notifInfo]; +} + +/* + Helper methods +*/ ++ (void)dispatchLocalNotificationFromNotification:(NSDictionary *)notification { + NSDictionary *managedAps = [notification objectForKey:@"managedAps"]; + NSDictionary *alert = [managedAps objectForKey:@"alert"]; + NSString *action = [managedAps objectForKey:@"action"]; + NSString *notificationId = [managedAps objectForKey:@"notificationId"]; + + if ([action isEqualToString:CommIOSNotificationsCreateAction] && + notificationId && alert) { + + // trigger new client push notification + UILocalNotification *note = [UILocalNotification new]; + note.alertTitle = [alert objectForKey:@"title"]; + note.alertBody = [alert objectForKey:@"body"]; + note.userInfo = notification; + note.soundName = [managedAps objectForKey:@"sound"]; + note.category = [managedAps objectForKey:@"category"]; + + [[UIApplication sharedApplication] presentLocalNotificationNow:note]; + + // Serialize it and store so we can delete it later + NSData *data = [NSKeyedArchiver archivedDataWithRootObject:note]; + NSString *notificationKey = + [self buildNotificationKeyfromNotification:notificationId]; + [[NSUserDefaults standardUserDefaults] setObject:data + forKey:notificationKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; + + NSLog(@"Local notification was triggered: %@", notificationKey); + } +} + ++ (NSString *)buildNotificationKeyfromNotification:(NSString *)notificationId { + return [NSString stringWithFormat:@"%@.%@", + [[NSBundle mainBundle] bundleIdentifier], + notificationId]; +} + ++ (NSString *)deviceTokenToString:(NSData *)deviceToken { + NSMutableString *result = [NSMutableString string]; + NSUInteger deviceTokenLength = deviceToken.length; + const unsigned char *bytes = (const unsigned char *)deviceToken.bytes; + for (NSUInteger i = 0; i < deviceTokenLength; i++) { + [result appendFormat:@"%02x", bytes[i]]; + } + + return [result copy]; +} + @end