From d945506c827aa28614837e1458aca72cc7c7d0ff Mon Sep 17 00:00:00 2001 From: Ryan Eberhardt Date: Thu, 10 Aug 2017 11:23:21 -0700 Subject: [PATCH] Use UserNotifications to create local notifications a15e038a adds support for enumerating/dismissing delivered notifications by ID, but UserNotifications notifications and UILocalNotification notifications have a different set of IDs (a notification created using UILocalNotification will have an ID that does not match the notification's ID in the UserNotifications world and hence cannot be used to dismiss that notification). This commit creates the notifications using UserNotifications if the user is on iOS 10. --- README.md | 29 ++++++++---- RNNotifications/RNNotifications.m | 78 ++++++++++++++++++++++++------- index.ios.js | 4 +- 3 files changed, 84 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index e544f8c..641a329 100644 --- a/README.md +++ b/README.md @@ -326,7 +326,6 @@ Example: let localNotification = NotificationsIOS.localNotification({ alertBody: "Local notificiation!", alertTitle: "Local Notification Title", - alertAction: "Click here to open", soundName: "chime.aiff", category: "SOME_CATEGORY", userInfo: { } @@ -338,7 +337,7 @@ Notification object contains: - **`fireDate`**- The date and time when the system should deliver the notification (optinal - default is immidiate dispatch). - `alertBody`- The message displayed in the notification alert. - `alertTitle`- The title of the notification, displayed in the notifications center. -- `alertAction`- The "action" displayed beneath an actionable notification. +- `alertAction`- The "action" displayed beneath an actionable notification on the lockscreen (e.g. "Slide to **open**"). Note that Apple no longer shows this in iOS 10. - `soundName`- The sound played when the notification is fired (optional). - `category`- The category of this notification, required for [interactive notifications](#interactive--actionable-notifications-ios-only) (optional). - `userInfo`- An optional object containing additional notification data. @@ -357,8 +356,9 @@ NotificationsAndroid.localNotification({ Upon notification opening (tapping by the device user), all data fields will be delivered as-is). -### Cancel Local Notification -The `NotificationsIOS.localNotification()` and `NotificationsAndroid.localNotification()` methods return unique `notificationId` values, which can be used in order to cancel specific local notifications. You can cancel local notification by calling `NotificationsIOS.cancelLocalNotification(notificationId)` or `NotificationsAndroid.cancelLocalNotification(notificationId)`. +### Cancel Scheduled Local Notifications + +The `NotificationsIOS.localNotification()` and `NotificationsAndroid.localNotification()` methods return unique `notificationId` values, which can be used in order to cancel specific local notifications that were scheduled for delivery on `fireDate` and have not yet been delivered. You can cancel local notification by calling `NotificationsIOS.cancelLocalNotification(notificationId)` or `NotificationsAndroid.cancelLocalNotification(notificationId)`. Example (iOS): @@ -366,7 +366,6 @@ Example (iOS): let someLocalNotification = NotificationsIOS.localNotification({ alertBody: "Local notificiation!", alertTitle: "Local Notification Title", - alertAction: "Click here to open", soundName: "chime.aiff", category: "SOME_CATEGORY", userInfo: { } @@ -375,12 +374,26 @@ let someLocalNotification = NotificationsIOS.localNotification({ NotificationsIOS.cancelLocalNotification(someLocalNotification); ``` -### Cancel All Local Notifications (iOS-only!) +To cancel all local notifications (**iOS only!**), use `cancelAllLocalNotifications()`: ```javascript NotificationsIOS.cancelAllLocalNotifications(); ``` +### Cancel Delivered Local Notifications (iOS 10+ only) + +To dismiss notifications from the notification center that have already been shown to the user, call `NotificationsIOS.removeDeliveredNotifications([notificationId])`: + +```javascript +let someLocalNotification = NotificationsIOS.localNotification({...}); + +NotificationsIOS.removeDeliveredNotifications([someLocalNotification]); +``` + +Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications +(note that this will dismiss push notifications in addition to local +notifications). + --- ## Managed Notifications (iOS only) @@ -444,13 +457,13 @@ Now the server should push the notification a bit differently- background instea ### getDeliveredNotifications -`PushNotification.getDeliveredNotifications(callback: (notifications: [Object]) => void)` +`PushNotification.getDeliveredNotifications(callback: (notifications: Array) => void)` Provides you with a list of the app’s notifications that are still displayed in Notification Center. ### removeDeliveredNotifications -`PushNotification.removeDeliveredNotifications(identifiers: [string])` +`PushNotification.removeDeliveredNotifications(identifiers: Array)` Removes the specified notifications from Notification Center. diff --git a/RNNotifications/RNNotifications.m b/RNNotifications/RNNotifications.m index 304758c..3c12a9d 100644 --- a/RNNotifications/RNNotifications.m +++ b/RNNotifications/RNNotifications.m @@ -17,6 +17,8 @@ #import "RNNotificationsBridgeQueue.h" #import +#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending) + NSString* const RNNotificationCreateAction = @"CREATE"; NSString* const RNNotificationClearAction = @"CLEAR"; @@ -108,26 +110,58 @@ RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ } @end +@implementation RCTConvert (UNNotificationRequest) ++ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSString*)notificationId +{ + NSDictionary *details = [self NSDictionary:json]; + + UNMutableNotificationContent *content = [UNMutableNotificationContent new]; + content.body = [RCTConvert NSString:details[@"alertBody"]]; + content.title = [RCTConvert NSString:details[@"alertTitle"]]; + content.sound = [RCTConvert NSString:details[@"soundName"]] + ? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"soundName"]]] + : [UNNotificationSound defaultSound]; + content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{}; + content.categoryIdentifier = [RCTConvert NSString:details[@"category"]]; + + NSDate *triggerDate = [RCTConvert NSDate:details[@"fireDate"]]; + UNCalendarNotificationTrigger *trigger = nil; + if (triggerDate != nil) { + NSDateComponents *triggerDateComponents = [[NSCalendar currentCalendar] + components:NSCalendarUnitYear + + NSCalendarUnitMonth + NSCalendarUnitDay + + NSCalendarUnitHour + NSCalendarUnitMinute + + NSCalendarUnitSecond + NSCalendarUnitTimeZone + fromDate:triggerDate]; + trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDateComponents + repeats:NO]; + } + + return [UNNotificationRequest requestWithIdentifier:notificationId + content:content trigger:trigger]; +} +@end + static NSDictionary *RCTFormatUNNotification(UNNotification *notification) { NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary]; UNNotificationContent *content = notification.request.content; formattedNotification[@"identifier"] = notification.request.identifier; - + if (notification.date) { NSDateFormatter *formatter = [NSDateFormatter new]; [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; NSString *dateString = [formatter stringFromDate:notification.date]; formattedNotification[@"fireDate"] = dateString; } - + formattedNotification[@"alertTitle"] = RCTNullIfNil(content.title); formattedNotification[@"alertBody"] = RCTNullIfNil(content.body); formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier); formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier); formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo)); - + return formattedNotification; } @@ -545,25 +579,35 @@ RCT_EXPORT_METHOD(consumeBackgroundQueue) RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId) { - UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification]; - NSMutableArray* userInfo = localNotification.userInfo.mutableCopy; - [userInfo setValue:notificationId forKey:@"__id"]; - localNotification.userInfo = userInfo; - - if ([notification objectForKey:@"fireDate"] != nil) { - [[UIApplication sharedApplication] scheduleLocalNotification:localNotification]; + if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) { + UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId]; + [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil]; } else { - [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification]; + UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification]; + NSMutableArray* userInfo = localNotification.userInfo.mutableCopy; + [userInfo setValue:notificationId forKey:@"__id"]; + localNotification.userInfo = userInfo; + + if ([notification objectForKey:@"fireDate"] != nil) { + [[UIApplication sharedApplication] scheduleLocalNotification:localNotification]; + } else { + [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification]; + } } } RCT_EXPORT_METHOD(cancelLocalNotification:(NSString *)notificationId) { - for (UILocalNotification* notification in [UIApplication sharedApplication].scheduledLocalNotifications) { - NSDictionary* notificationInfo = notification.userInfo; + if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center removePendingNotificationRequestsWithIdentifiers:@[notificationId]]; + } else { + for (UILocalNotification* notification in [UIApplication sharedApplication].scheduledLocalNotifications) { + NSDictionary* notificationInfo = notification.userInfo; - if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) { - [[UIApplication sharedApplication] cancelLocalNotification:notification]; + if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) { + [[UIApplication sharedApplication] cancelLocalNotification:notification]; + } } } } @@ -576,7 +620,7 @@ RCT_EXPORT_METHOD(cancelAllLocalNotifications) RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { BOOL ans; - + if (TARGET_IPHONE_SIMULATOR) { ans = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != 0; } @@ -620,7 +664,7 @@ RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback) UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center getDeliveredNotificationsWithCompletionHandler:^(NSArray * _Nonnull notifications) { NSMutableArray *formattedNotifications = [NSMutableArray new]; - + for (UNNotification *notification in notifications) { [formattedNotifications addObject:RCTFormatUNNotification(notification)]; } diff --git a/index.ios.js b/index.ios.js index 210e951..d5ac856 100644 --- a/index.ios.js +++ b/index.ios.js @@ -229,7 +229,7 @@ export default class NotificationsIOS { * * @param identifiers Array of notification identifiers */ - static removeDeliveredNotifications(identifiers: [string]) { + static removeDeliveredNotifications(identifiers: Array) { return NativeRNNotifications.removeDeliveredNotifications(identifiers); } @@ -248,7 +248,7 @@ export default class NotificationsIOS { * - `thread-id` : The thread identifier of this notification, if has one. * - `fireDate` : The date and time when the system should deliver the notification. if not specified, the notification will be dispatched immediately. */ - static getDeliveredNotifications(callback: (notifications: [Object]) => void) { + static getDeliveredNotifications(callback: (notifications: Array) => void) { return NativeRNNotifications.getDeliveredNotifications(callback); } } -- 2.26.2