From 6b911b207b4b2ccb4b0081f4924215f9c914211c Mon Sep 17 00:00:00 2001 From: Chris Bianca Date: Wed, 14 Feb 2018 09:07:29 +0000 Subject: [PATCH] [notifications] Start iOS implementation of notifications --- .../messaging/RNFirebaseMessaging.m | 6 +- .../notifications/RNFirebaseNotifications.m | 243 +++++++++++++++++- lib/modules/notifications/index.js | 12 +- 3 files changed, 252 insertions(+), 9 deletions(-) diff --git a/ios/RNFirebase/messaging/RNFirebaseMessaging.m b/ios/RNFirebase/messaging/RNFirebaseMessaging.m index 7a2ebe39..14454ef0 100644 --- a/ios/RNFirebase/messaging/RNFirebaseMessaging.m +++ b/ios/RNFirebase/messaging/RNFirebaseMessaging.m @@ -15,11 +15,13 @@ // notifications via APNS #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 @import UserNotifications; - @interface RNFirebaseMessaging () +#else +@interface RNFirebaseMessaging () +#endif @property (nonatomic, strong) NSMutableDictionary *callbackHandlers; @end -#endif + @implementation RNFirebaseMessaging diff --git a/ios/RNFirebase/notifications/RNFirebaseNotifications.m b/ios/RNFirebase/notifications/RNFirebaseNotifications.m index daf2cf06..dec1b55d 100644 --- a/ios/RNFirebase/notifications/RNFirebaseNotifications.m +++ b/ios/RNFirebase/notifications/RNFirebaseNotifications.m @@ -1,20 +1,255 @@ #import "RNFirebaseNotifications.h" #if __has_include() +#import + +#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 +@import UserNotifications; +#endif @implementation RNFirebaseNotifications RCT_EXPORT_MODULE(); +RCT_EXPORT_METHOD(cancelAllNotifications) { + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + [RCTSharedApplication() cancelAllLocalNotifications]; + } else { + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; + if (notificationCenter != nil) { + [[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests]; + } + #endif + } +} + +RCT_EXPORT_METHOD(cancelNotification:(NSString*) notificationId) { + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) { + NSDictionary *notificationInfo = notification.userInfo; + if ([notificationId isEqualToString:[notificationInfo valueForKey:@"notificationId"]]) { + [RCTSharedApplication() cancelLocalNotification:notification]; + } + } + } else { + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; + if (notificationCenter != nil) { + [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[notificationId]]; + } + #endif + } +} + +RCT_EXPORT_METHOD(displayNotification:(NSDictionary*) notification + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + UILocalNotification* notif = [self buildUILocalNotification:notification]; + [RCTSharedApplication() presentLocalNotificationNow:notif]; + resolve(nil); + } else { + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + UNNotificationRequest* request = [self buildUNNotificationRequest:notification]; + [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { + if (!error) { + resolve(nil); + }else{ + reject(@"notifications/display_notification_error", @"Failed to display notificaton", error); + } + }]; + #endif + } +} + RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){ - UILocalNotification *localUserInfo = [self bridge].launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; - if (localUserInfo) { - // TODO: Proper format - resolve([[localUserInfo userInfo] copy]); + UILocalNotification *localNotification = [self bridge].launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; + if (localNotification) { + NSDictionary *notification = [self parseUILocalNotification:localNotification]; + resolve(notification); } else { resolve(nil); } } +RCT_EXPORT_METHOD(getScheduledNotifications:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + NSMutableArray* notifications = [[NSMutableArray alloc] init]; + for (UILocalNotification *notif in [RCTSharedApplication() scheduledLocalNotifications]){ + NSDictionary *notification = [self parseUILocalNotification:notif]; + [notifications addObject:notification]; + } + resolve(notifications); + } else { + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + [[UNUserNotificationCenter currentNotificationCenter] getPendingNotificationRequestsWithCompletionHandler:^(NSArray * _Nonnull requests) { + NSMutableArray* notifications = [[NSMutableArray alloc] init]; + for (UNNotificationRequest *notif in requests){ + NSDictionary *notification = [self parseUNNotificationRequest:notif]; + [notifications addObject:notification]; + } + resolve(notifications); + }]; + #endif + } +} + +RCT_EXPORT_METHOD(removeAllDeliveredNotifications) { + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + // No such functionality on iOS 8/9 + } else { + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; + if (notificationCenter != nil) { + [[UNUserNotificationCenter currentNotificationCenter] removeAllDeliveredNotifications]; + } + #endif + } +} + +RCT_EXPORT_METHOD(removeDeliveredNotification:(NSString*) notificationId) { + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + // No such functionality on iOS 8/9 + } else { + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; + if (notificationCenter != nil) { + [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:@[notificationId]]; + } + #endif + } +} + +RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification + schedule:(NSDictionary*) schedule + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) { + if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { + UILocalNotification* notif = [self buildUILocalNotification:notification]; + // TODO: Schedule + [RCTSharedApplication() scheduleLocalNotification:notif]; + resolve(nil); + } else { + #if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 + UNNotificationRequest* request = [self buildUNNotificationRequest:notification]; + // TODO: Schedule + [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { + if (!error) { + resolve(nil); + }else{ + reject(@"notification/schedule_notification_error", @"Failed to schedule notificaton", error); + } + }]; + #endif + } +} + +- (UILocalNotification*) buildUILocalNotification:(NSDictionary *) notification { + UILocalNotification *localNotification = [[UILocalNotification alloc] init]; + if (notification[@"body"]) { + localNotification.alertBody = notification[@"body"]; + } + if (notification[@"data"]) { + localNotification.userInfo = notification[@"data"]; + } + if (notification[@"sound"]) { + localNotification.soundName = notification[@"sound"]; + } + if (notification[@"title"]) { + localNotification.alertTitle = notification[@"title"]; + } + if (notification[@"ios"]) { + NSDictionary *ios = notification[@"ios"]; + if (ios[@"alertAction"]) { + localNotification.alertAction = ios[@"alertAction"]; + } + if (ios[@"badge"]) { + NSNumber *badge = ios[@"badge"]; + localNotification.applicationIconBadgeNumber = badge.integerValue; + } + if (ios[@"category"]) { + localNotification.category = ios[@"category"]; + } + if (ios[@"hasAction"]) { + localNotification.hasAction = ios[@"hasAction"]; + } + if (ios[@"launchImage"]) { + localNotification.alertLaunchImage = ios[@"launchImage"]; + } + } + + return localNotification; +} + +- (UNNotificationRequest*) buildUNNotificationRequest:(NSDictionary *) notification { + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + if (notification[@"body"]) { + content.body = notification[@"body"]; + } + if (notification[@"data"]) { + content.userInfo = notification[@"data"]; + } + if (notification[@"sound"]) { + content.sound = notification[@"sound"]; + } + if (notification[@"subtitle"]) { + content.title = notification[@"subtitle"]; + } + if (notification[@"title"]) { + content.title = notification[@"title"]; + } + if (notification[@"ios"]) { + NSDictionary *ios = notification[@"ios"]; + if (ios[@"attachments"]) { + NSMutableArray *attachments = [[NSMutableArray alloc] init]; + for (NSDictionary *a in ios[@"attachments"]) { + NSString *identifier = a[@"identifier"]; + NSURL *url = [NSURL URLWithString:a[@"url"]]; + NSDictionary *options = a[@"options"]; + + NSError *error; + UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:url options:options error:&error]; + if (attachment) { + [attachments addObject:attachment]; + } else { + NSLog(@"Failed to create attachment: %@", error); + } + } + content.attachments = attachments; + } + + if (ios[@"badge"]) { + content.badge = ios[@"badge"]; + } + if (ios[@"category"]) { + content.categoryIdentifier = ios[@"category"]; + } + if (ios[@"launchImage"]) { + content.launchImageName = ios[@"launchImage"]; + } + if (ios[@"threadIdentifier"]) { + content.threadIdentifier = ios[@"threadIdentifier"]; + } + } + + // TODO: Scheduling + return [UNNotificationRequest requestWithIdentifier:notification[@"ios"][@"identifier"] content:content trigger:nil]; +} + +- (NSDictionary*) parseUILocalNotification:(UILocalNotification *) localNotification { + NSMutableDictionary *notification = [[NSMutableDictionary alloc] init]; + + + + return notification; + // TODO +} + +- (NSDictionary*) parseUNNotificationRequest:(UNNotificationRequest *) localNotification { + // TODO +} + - (NSArray *)supportedEvents { return @[]; } diff --git a/lib/modules/notifications/index.js b/lib/modules/notifications/index.js index 84fb39ab..c0cda1b5 100644 --- a/lib/modules/notifications/index.js +++ b/lib/modules/notifications/index.js @@ -73,7 +73,7 @@ export default class Notifications extends ModuleBase { } cancelAllNotifications(): Promise { - return getNativeModule(this).cancelAllLocalNotifications(); + return getNativeModule(this).cancelAllNotifications(); } /** @@ -85,7 +85,7 @@ export default class Notifications extends ModuleBase { if (!notificationId) { return Promise.reject(new Error('Missing notificationId')); } - return getNativeModule(this).cancelLocalNotification(notificationId); + return getNativeModule(this).cancelNotification(notificationId); } /** @@ -102,12 +102,18 @@ export default class Notifications extends ModuleBase { return getNativeModule(this).displayNotification(notification.build()); } + getInitialNotification(): Promise { + return getNativeModule(this).getInitialNotification(); + // TODO + // .then(notification => (notification ? new Notification(this, notification) : null)); + } + /** * Returns an array of all scheduled notifications * @returns {Promise.} */ getScheduledNotifications(): Promise { - return getNativeModule(this).getScheduledLocalNotifications(); + return getNativeModule(this).getScheduledNotifications(); } onNotification(