Skip to content

Commit e4921f3

Browse files
authored
Merge pull request wix#103 from reberhardt7/dismiss-notifications
Use UserNotifications to create/dismiss local notifications
2 parents 77bce6a + d945506 commit e4921f3

File tree

6 files changed

+251
-25
lines changed

6 files changed

+251
-25
lines changed

README.md

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,6 @@ Example:
326326
let localNotification = NotificationsIOS.localNotification({
327327
alertBody: "Local notificiation!",
328328
alertTitle: "Local Notification Title",
329-
alertAction: "Click here to open",
330329
soundName: "chime.aiff",
331330
category: "SOME_CATEGORY",
332331
userInfo: { }
@@ -338,7 +337,7 @@ Notification object contains:
338337
- **`fireDate`**- The date and time when the system should deliver the notification (optinal - default is immidiate dispatch).
339338
- `alertBody`- The message displayed in the notification alert.
340339
- `alertTitle`- The title of the notification, displayed in the notifications center.
341-
- `alertAction`- The "action" displayed beneath an actionable notification.
340+
- `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.
342341
- `soundName`- The sound played when the notification is fired (optional).
343342
- `category`- The category of this notification, required for [interactive notifications](#interactive--actionable-notifications-ios-only) (optional).
344343
- `userInfo`- An optional object containing additional notification data.
@@ -357,16 +356,16 @@ NotificationsAndroid.localNotification({
357356

358357
Upon notification opening (tapping by the device user), all data fields will be delivered as-is).
359358

360-
### Cancel Local Notification
361-
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)`.
359+
### Cancel Scheduled Local Notifications
360+
361+
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)`.
362362

363363
Example (iOS):
364364

365365
```javascript
366366
let someLocalNotification = NotificationsIOS.localNotification({
367367
alertBody: "Local notificiation!",
368368
alertTitle: "Local Notification Title",
369-
alertAction: "Click here to open",
370369
soundName: "chime.aiff",
371370
category: "SOME_CATEGORY",
372371
userInfo: { }
@@ -375,12 +374,26 @@ let someLocalNotification = NotificationsIOS.localNotification({
375374
NotificationsIOS.cancelLocalNotification(someLocalNotification);
376375
```
377376

378-
### Cancel All Local Notifications (iOS-only!)
377+
To cancel all local notifications (**iOS only!**), use `cancelAllLocalNotifications()`:
379378

380379
```javascript
381380
NotificationsIOS.cancelAllLocalNotifications();
382381
```
383382

383+
### Cancel Delivered Local Notifications (iOS 10+ only)
384+
385+
To dismiss notifications from the notification center that have already been shown to the user, call `NotificationsIOS.removeDeliveredNotifications([notificationId])`:
386+
387+
```javascript
388+
let someLocalNotification = NotificationsIOS.localNotification({...});
389+
390+
NotificationsIOS.removeDeliveredNotifications([someLocalNotification]);
391+
```
392+
393+
Call `removeAllDeliveredNotifications()` to dismiss all delivered notifications
394+
(note that this will dismiss push notifications in addition to local
395+
notifications).
396+
384397
---
385398

386399
## Managed Notifications (iOS only)
@@ -440,6 +453,28 @@ Now the server should push the notification a bit differently- background instea
440453
441454
---
442455
456+
## Remove notifications (iOS only)
457+
458+
### getDeliveredNotifications
459+
460+
`PushNotification.getDeliveredNotifications(callback: (notifications: Array<Object>) => void)`
461+
462+
Provides you with a list of the app’s notifications that are still displayed in Notification Center.
463+
464+
### removeDeliveredNotifications
465+
466+
`PushNotification.removeDeliveredNotifications(identifiers: Array<String>)`
467+
468+
Removes the specified notifications from Notification Center.
469+
470+
### removeAllDeliveredNotifications
471+
472+
`PushNotification.removeAllDeliveredNotifications()`
473+
474+
Removes all delivered notifications from Notification Center.
475+
476+
---
477+
443478
## PushKit API (iOS only)
444479
445480
The PushKit framework provides the classes for your iOS apps to receive background pushes from remote servers. it has better support for background notifications compared to regular push notifications with `content-available: 1`. More info in [iOS PushKit documentation](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Reference/PushKit_Framework/).

RNNotifications/RNNotifications.m

Lines changed: 116 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
#import "RCTUtils.h"
1616
#endif
1717
#import "RNNotificationsBridgeQueue.h"
18+
#import <UserNotifications/UserNotifications.h>
19+
20+
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
1821

1922
NSString* const RNNotificationCreateAction = @"CREATE";
2023
NSString* const RNNotificationClearAction = @"CLEAR";
@@ -107,6 +110,61 @@ + (UILocalNotification *)UILocalNotification:(id)json
107110
}
108111
@end
109112

113+
@implementation RCTConvert (UNNotificationRequest)
114+
+ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSString*)notificationId
115+
{
116+
NSDictionary<NSString *, id> *details = [self NSDictionary:json];
117+
118+
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
119+
content.body = [RCTConvert NSString:details[@"alertBody"]];
120+
content.title = [RCTConvert NSString:details[@"alertTitle"]];
121+
content.sound = [RCTConvert NSString:details[@"soundName"]]
122+
? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"soundName"]]]
123+
: [UNNotificationSound defaultSound];
124+
content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
125+
content.categoryIdentifier = [RCTConvert NSString:details[@"category"]];
126+
127+
NSDate *triggerDate = [RCTConvert NSDate:details[@"fireDate"]];
128+
UNCalendarNotificationTrigger *trigger = nil;
129+
if (triggerDate != nil) {
130+
NSDateComponents *triggerDateComponents = [[NSCalendar currentCalendar]
131+
components:NSCalendarUnitYear +
132+
NSCalendarUnitMonth + NSCalendarUnitDay +
133+
NSCalendarUnitHour + NSCalendarUnitMinute +
134+
NSCalendarUnitSecond + NSCalendarUnitTimeZone
135+
fromDate:triggerDate];
136+
trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDateComponents
137+
repeats:NO];
138+
}
139+
140+
return [UNNotificationRequest requestWithIdentifier:notificationId
141+
content:content trigger:trigger];
142+
}
143+
@end
144+
145+
static NSDictionary *RCTFormatUNNotification(UNNotification *notification)
146+
{
147+
NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary];
148+
UNNotificationContent *content = notification.request.content;
149+
150+
formattedNotification[@"identifier"] = notification.request.identifier;
151+
152+
if (notification.date) {
153+
NSDateFormatter *formatter = [NSDateFormatter new];
154+
[formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
155+
NSString *dateString = [formatter stringFromDate:notification.date];
156+
formattedNotification[@"fireDate"] = dateString;
157+
}
158+
159+
formattedNotification[@"alertTitle"] = RCTNullIfNil(content.title);
160+
formattedNotification[@"alertBody"] = RCTNullIfNil(content.body);
161+
formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier);
162+
formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier);
163+
formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo));
164+
165+
return formattedNotification;
166+
}
167+
110168
@implementation RNNotifications
111169

112170
RCT_EXPORT_MODULE()
@@ -521,25 +579,35 @@ - (void)handleNotificationActionTriggered:(NSNotification *)notification
521579

522580
RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId)
523581
{
524-
UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification];
525-
NSMutableArray* userInfo = localNotification.userInfo.mutableCopy;
526-
[userInfo setValue:notificationId forKey:@"__id"];
527-
localNotification.userInfo = userInfo;
528-
529-
if ([notification objectForKey:@"fireDate"] != nil) {
530-
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
582+
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) {
583+
UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId];
584+
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil];
531585
} else {
532-
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
586+
UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification];
587+
NSMutableArray* userInfo = localNotification.userInfo.mutableCopy;
588+
[userInfo setValue:notificationId forKey:@"__id"];
589+
localNotification.userInfo = userInfo;
590+
591+
if ([notification objectForKey:@"fireDate"] != nil) {
592+
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
593+
} else {
594+
[[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
595+
}
533596
}
534597
}
535598

536599
RCT_EXPORT_METHOD(cancelLocalNotification:(NSString *)notificationId)
537600
{
538-
for (UILocalNotification* notification in [UIApplication sharedApplication].scheduledLocalNotifications) {
539-
NSDictionary* notificationInfo = notification.userInfo;
601+
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) {
602+
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
603+
[center removePendingNotificationRequestsWithIdentifiers:@[notificationId]];
604+
} else {
605+
for (UILocalNotification* notification in [UIApplication sharedApplication].scheduledLocalNotifications) {
606+
NSDictionary* notificationInfo = notification.userInfo;
540607

541-
if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) {
542-
[[UIApplication sharedApplication] cancelLocalNotification:notification];
608+
if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) {
609+
[[UIApplication sharedApplication] cancelLocalNotification:notification];
610+
}
543611
}
544612
}
545613
}
@@ -552,7 +620,7 @@ - (void)handleNotificationActionTriggered:(NSNotification *)notification
552620
RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
553621
{
554622
BOOL ans;
555-
623+
556624
if (TARGET_IPHONE_SIMULATOR) {
557625
ans = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != 0;
558626
}
@@ -572,4 +640,39 @@ - (void)handleNotificationActionTriggered:(NSNotification *)notification
572640
});
573641
}
574642

643+
#if !TARGET_OS_TV
644+
645+
RCT_EXPORT_METHOD(removeAllDeliveredNotifications)
646+
{
647+
if ([UNUserNotificationCenter class]) {
648+
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
649+
[center removeAllDeliveredNotifications];
650+
}
651+
}
652+
653+
RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray<NSString *> *)identifiers)
654+
{
655+
if ([UNUserNotificationCenter class]) {
656+
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
657+
[center removeDeliveredNotificationsWithIdentifiers:identifiers];
658+
}
659+
}
660+
661+
RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback)
662+
{
663+
if ([UNUserNotificationCenter class]) {
664+
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
665+
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
666+
NSMutableArray<NSDictionary *> *formattedNotifications = [NSMutableArray new];
667+
668+
for (UNNotification *notification in notifications) {
669+
[formattedNotifications addObject:RCTFormatUNNotification(notification)];
670+
}
671+
callback(@[formattedNotifications]);
672+
}];
673+
}
674+
}
675+
676+
#endif !TARGET_OS_TV
677+
575678
@end

index.ios.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,39 @@ export default class NotificationsIOS {
216216
static checkPermissions() {
217217
return NativeRNNotifications.checkPermissions();
218218
}
219+
220+
/**
221+
* Remove all delivered notifications from Notification Center
222+
*/
223+
static removeAllDeliveredNotifications() {
224+
return NativeRNNotifications.removeAllDeliveredNotifications();
225+
}
226+
227+
/**
228+
* Removes the specified notifications from Notification Center
229+
*
230+
* @param identifiers Array of notification identifiers
231+
*/
232+
static removeDeliveredNotifications(identifiers: Array<String>) {
233+
return NativeRNNotifications.removeDeliveredNotifications(identifiers);
234+
}
235+
236+
/**
237+
* Provides you with a list of the app’s notifications that are still displayed in Notification Center
238+
*
239+
* @param callback Function which receive an array of delivered notifications
240+
*
241+
* A delivered notification is an object containing:
242+
*
243+
* - `identifier` : The identifier of this notification.
244+
* - `alertBody` : The message displayed in the notification alert.
245+
* - `alertTitle` : The message title displayed in the notification.
246+
* - `category` : The category of this notification, if has one.
247+
* - `userInfo` : An optional object containing additional notification data.
248+
* - `thread-id` : The thread identifier of this notification, if has one.
249+
* - `fireDate` : The date and time when the system should deliver the notification. if not specified, the notification will be dispatched immediately.
250+
*/
251+
static getDeliveredNotifications(callback: (notifications: Array<Object>) => void) {
252+
return NativeRNNotifications.getDeliveredNotifications(callback);
253+
}
219254
}

notification.ios.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export default class IOSNotification {
55
_badge: number;
66
_category: string;
77
_type: string; // regular / managed
8+
_thread: string;
89

910
constructor(notification: Object) {
1011
this._data = {};
@@ -21,6 +22,7 @@ export default class IOSNotification {
2122
this._badge = notification.aps.badge;
2223
this._category = notification.managedAps.category;
2324
this._type = "managed";
25+
this._thread = notification.aps["thread-id"];
2426
} else if (
2527
notification.aps &&
2628
notification.aps.alert) {
@@ -30,6 +32,7 @@ export default class IOSNotification {
3032
this._badge = notification.aps.badge;
3133
this._category = notification.aps.category;
3234
this._type = "regular";
35+
this._thread = notification.aps["thread-id"];
3336
}
3437

3538
Object.keys(notification).filter(key => key !== "aps").forEach(key => {
@@ -60,4 +63,8 @@ export default class IOSNotification {
6063
getType(): ?string {
6164
return this._type;
6265
}
66+
67+
getThread(): ?string {
68+
return this._thread;
69+
}
6370
}

0 commit comments

Comments
 (0)