Add observers for local notifications

Summary:
This commit adds the delegate hooks so that local notifications get
passed onto the JS and adds a new event listener type for local
notifications.

Also add functions to clear local notifications
Closes https://github.com/facebook/react-native/pull/2084

Reviewed By: svcscm

Differential Revision: D2908096

Pulled By: nicklockwood

fb-gh-sync-id: 759d299ea35abea177e72934076297d666d3ea20
shipit-source-id: 759d299ea35abea177e72934076297d666d3ea20
This commit is contained in:
slycoder 2016-02-09 05:45:23 -08:00 committed by facebook-github-bot-7
parent c6366e4dd8
commit 758d9e8304
4 changed files with 89 additions and 27 deletions

View File

@ -21,6 +21,7 @@ var _initialNotification = RCTPushNotificationManager &&
var DEVICE_NOTIF_EVENT = 'remoteNotificationReceived'; var DEVICE_NOTIF_EVENT = 'remoteNotificationReceived';
var NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered'; var NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered';
var DEVICE_LOCAL_NOTIF_EVENT = 'localNotificationReceived';
/** /**
* Handle push notifications for your app, including permission handling and * Handle push notifications for your app, including permission handling and
@ -59,6 +60,11 @@ var NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered';
* { * {
* [RCTPushNotificationManager didReceiveRemoteNotification:notification]; * [RCTPushNotificationManager didReceiveRemoteNotification:notification];
* } * }
* // Required for the localNotification event.
* - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
* {
* [RCTPushNotificationManager didReceiveLocalNotification:notification];
* }
* ``` * ```
*/ */
class PushNotificationIOS { class PushNotificationIOS {
@ -74,7 +80,6 @@ class PushNotificationIOS {
* *
* - `alertBody` : The message displayed in the notification alert. * - `alertBody` : The message displayed in the notification alert.
* - `soundName` : The sound played when the notification is fired (optional). * - `soundName` : The sound played when the notification is fired (optional).
*
*/ */
static presentLocalNotification(details: Object) { static presentLocalNotification(details: Object) {
RCTPushNotificationManager.presentLocalNotification(details); RCTPushNotificationManager.presentLocalNotification(details);
@ -88,7 +93,7 @@ class PushNotificationIOS {
* - `fireDate` : The date and time when the system should deliver the notification. * - `fireDate` : The date and time when the system should deliver the notification.
* - `alertBody` : The message displayed in the notification alert. * - `alertBody` : The message displayed in the notification alert.
* - `soundName` : The sound played when the notification is fired (optional). * - `soundName` : The sound played when the notification is fired (optional).
* * - `userInfo` : An optional object containing additional notification data.
*/ */
static scheduleLocalNotification(details: Object) { static scheduleLocalNotification(details: Object) {
RCTPushNotificationManager.scheduleLocalNotification(details); RCTPushNotificationManager.scheduleLocalNotification(details);
@ -115,6 +120,17 @@ class PushNotificationIOS {
RCTPushNotificationManager.getApplicationIconBadgeNumber(callback); RCTPushNotificationManager.getApplicationIconBadgeNumber(callback);
} }
/**
* Cancel local notifications.
*
* Optionally restricts the set of canceled notifications to those
* notifications whose `userInfo` fields match the corresponding fields
* in the `userInfo` argument.
*/
static cancelLocalNotifications(userInfo: Object) {
RCTPushNotificationManager.cancelLocalNotifications(userInfo);
}
/** /**
* Attaches a listener to remote notification events while the app is running * Attaches a listener to remote notification events while the app is running
* in the foreground or the background. * in the foreground or the background.
@ -128,8 +144,8 @@ class PushNotificationIOS {
*/ */
static addEventListener(type: string, handler: Function) { static addEventListener(type: string, handler: Function) {
invariant( invariant(
type === 'notification' || type === 'register', type === 'notification' || type === 'register' || type === 'localNotification',
'PushNotificationIOS only supports `notification` and `register` events' 'PushNotificationIOS only supports `notification`, `register` and `localNotification` events'
); );
var listener; var listener;
if (type === 'notification') { if (type === 'notification') {
@ -139,6 +155,13 @@ class PushNotificationIOS {
handler(new PushNotificationIOS(notifData)); handler(new PushNotificationIOS(notifData));
} }
); );
} else if (type === 'localNotification') {
listener = RCTDeviceEventEmitter.addListener(
DEVICE_LOCAL_NOTIF_EVENT,
(notifData) => {
handler(new PushNotificationIOS(notifData));
}
);
} else if (type === 'register') { } else if (type === 'register') {
listener = RCTDeviceEventEmitter.addListener( listener = RCTDeviceEventEmitter.addListener(
NOTIF_REGISTER_EVENT, NOTIF_REGISTER_EVENT,
@ -220,8 +243,8 @@ class PushNotificationIOS {
*/ */
static removeEventListener(type: string, handler: Function) { static removeEventListener(type: string, handler: Function) {
invariant( invariant(
type === 'notification' || type === 'register', type === 'notification' || type === 'register' || type === 'localNotification',
'PushNotificationIOS only supports `notification` and `register` events' 'PushNotificationIOS only supports `notification`, `register` and `localNotification` events'
); );
var listener = _notifHandlers.get(handler); var listener = _notifHandlers.get(handler);
if (!listener) { if (!listener) {

View File

@ -16,5 +16,6 @@
+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; + (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification; + (void)didReceiveRemoteNotification:(NSDictionary *)notification;
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification;
@end @end

View File

@ -24,6 +24,7 @@
#endif #endif
NSString *const RCTLocalNotificationReceived = @"LocalNotificationReceived";
NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived"; NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived";
NSString *const RCTRemoteNotificationsRegistered = @"RemoteNotificationsRegistered"; NSString *const RCTRemoteNotificationsRegistered = @"RemoteNotificationsRegistered";
@ -36,6 +37,7 @@ NSString *const RCTRemoteNotificationsRegistered = @"RemoteNotificationsRegister
notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]] ?: [NSDate date]; notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]] ?: [NSDate date];
notification.alertBody = [RCTConvert NSString:details[@"alertBody"]]; notification.alertBody = [RCTConvert NSString:details[@"alertBody"]];
notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName; notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName;
notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]];
return notification; return notification;
} }
@ -56,6 +58,10 @@ RCT_EXPORT_MODULE()
{ {
_bridge = bridge; _bridge = bridge;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleLocalNotificationReceived:)
name:RCTLocalNotificationReceived
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self [[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleRemoteNotificationReceived:) selector:@selector(handleRemoteNotificationReceived:)
name:RCTRemoteNotificationReceived name:RCTRemoteNotificationReceived
@ -68,7 +74,8 @@ RCT_EXPORT_MODULE()
- (NSDictionary<NSString *, id> *)constantsToExport - (NSDictionary<NSString *, id> *)constantsToExport
{ {
NSDictionary<NSString *, id> *initialNotification = [_bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] copy]; NSDictionary<NSString *, id> *initialNotification =
[_bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] copy];
return @{@"initialNotification": RCTNullIfNil(initialNotification)}; return @{@"initialNotification": RCTNullIfNil(initialNotification)};
} }
@ -87,12 +94,9 @@ RCT_EXPORT_MODULE()
for (NSUInteger i = 0; i < deviceTokenLength; i++) { for (NSUInteger i = 0; i < deviceTokenLength; i++) {
[hexString appendFormat:@"%02x", bytes[i]]; [hexString appendFormat:@"%02x", bytes[i]];
} }
NSDictionary<NSString *, id> *userInfo = @{
@"deviceToken" : [hexString copy]
};
[[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationsRegistered [[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationsRegistered
object:self object:self
userInfo:userInfo]; userInfo:@{@"deviceToken" : [hexString copy]}];
} }
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification + (void)didReceiveRemoteNotification:(NSDictionary *)notification
@ -102,6 +106,26 @@ RCT_EXPORT_MODULE()
userInfo:notification]; userInfo:notification];
} }
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification
{
NSMutableDictionary *details = [NSMutableDictionary new];
if (notification.alertBody) {
details[@"alertBody"] = notification.alertBody;
}
if (notification.userInfo) {
details[@"userInfo"] = RCTJSONClean(notification.userInfo);
}
[[NSNotificationCenter defaultCenter] postNotificationName:RCTLocalNotificationReceived
object:self
userInfo:details];
}
- (void)handleLocalNotificationReceived:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"localNotificationReceived"
body:notification.userInfo];
}
- (void)handleRemoteNotificationReceived:(NSNotification *)notification - (void)handleRemoteNotificationReceived:(NSNotification *)notification
{ {
[_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationReceived" [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationReceived"
@ -127,9 +151,7 @@ RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(NSInteger)number)
*/ */
RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback) RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback)
{ {
callback(@[ callback(@[@(RCTSharedApplication().applicationIconBadgeNumber)]);
@(RCTSharedApplication().applicationIconBadgeNumber)
]);
} }
RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions) RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions)
@ -140,13 +162,13 @@ RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions)
UIUserNotificationType types = UIUserNotificationTypeNone; UIUserNotificationType types = UIUserNotificationTypeNone;
if (permissions) { if (permissions) {
if ([permissions[@"alert"] boolValue]) { if ([RCTConvert BOOL:permissions[@"alert"]]) {
types |= UIUserNotificationTypeAlert; types |= UIUserNotificationTypeAlert;
} }
if ([permissions[@"badge"] boolValue]) { if ([RCTConvert BOOL:permissions[@"badge"]]) {
types |= UIUserNotificationTypeBadge; types |= UIUserNotificationTypeBadge;
} }
if ([permissions[@"sound"] boolValue]) { if ([RCTConvert BOOL:permissions[@"sound"]]) {
types |= UIUserNotificationTypeSound; types |= UIUserNotificationTypeSound;
} }
} else { } else {
@ -155,7 +177,8 @@ RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions)
UIApplication *app = RCTSharedApplication(); UIApplication *app = RCTSharedApplication();
if ([app respondsToSelector:@selector(registerUserNotificationSettings:)]) { if ([app respondsToSelector:@selector(registerUserNotificationSettings:)]) {
UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:(NSUInteger)types categories:nil]; UIUserNotificationSettings *notificationSettings =
[UIUserNotificationSettings settingsForTypes:(NSUInteger)types categories:nil];
[app registerUserNotificationSettings:notificationSettings]; [app registerUserNotificationSettings:notificationSettings];
[app registerForRemoteNotifications]; [app registerForRemoteNotifications];
} else { } else {
@ -171,8 +194,7 @@ RCT_EXPORT_METHOD(abandonPermissions)
RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback) RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback)
{ {
if (RCTRunningInAppExtension()) { if (RCTRunningInAppExtension()) {
NSDictionary<NSString *, NSNumber *> *permissions = @{@"alert": @NO, @"badge": @NO, @"sound": @NO}; callback(@[@{@"alert": @NO, @"badge": @NO, @"sound": @NO}]);
callback(@[permissions]);
return; return;
} }
@ -189,12 +211,11 @@ RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback)
} }
NSMutableDictionary<NSString *, NSNumber *> *permissions = [NSMutableDictionary new]; callback(@[@{
permissions[@"alert"] = @((types & UIUserNotificationTypeAlert) > 0); @"alert": @((types & UIUserNotificationTypeAlert) > 0),
permissions[@"badge"] = @((types & UIUserNotificationTypeBadge) > 0); @"badge": @((types & UIUserNotificationTypeBadge) > 0),
permissions[@"sound"] = @((types & UIUserNotificationTypeSound) > 0); @"sound": @((types & UIUserNotificationTypeSound) > 0),
}]);
callback(@[permissions]);
} }
RCT_EXPORT_METHOD(presentLocalNotification:(UILocalNotification *)notification) RCT_EXPORT_METHOD(presentLocalNotification:(UILocalNotification *)notification)
@ -212,4 +233,21 @@ RCT_EXPORT_METHOD(cancelAllLocalNotifications)
[RCTSharedApplication() cancelAllLocalNotifications]; [RCTSharedApplication() cancelAllLocalNotifications];
} }
RCT_EXPORT_METHOD(cancelLocalNotifications:(NSDictionary *)userInfo)
{
for (UILocalNotification *notification in [UIApplication sharedApplication].scheduledLocalNotifications) {
__block BOOL matchesAll = YES;
NSDictionary *notificationInfo = notification.userInfo;
[userInfo enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
if (![notificationInfo[key] isEqual:obj]) {
matchesAll = NO;
*stop = YES;
}
}];
if (matchesAll) {
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
}
}
@end @end

View File

@ -23,7 +23,7 @@ RCT_EXTERN NSString *__nullable RCTJSONStringify(id __nullable jsonObject, NSErr
RCT_EXTERN id __nullable RCTJSONParse(NSString *__nullable jsonString, NSError **error); RCT_EXTERN id __nullable RCTJSONParse(NSString *__nullable jsonString, NSError **error);
RCT_EXTERN id __nullable RCTJSONParseMutable(NSString *__nullable jsonString, NSError **error); RCT_EXTERN id __nullable RCTJSONParseMutable(NSString *__nullable jsonString, NSError **error);
// Santize a JSON string by stripping invalid objects and/or NaN values // Sanitize a JSON object by stripping invalid types and/or NaN values
RCT_EXTERN id RCTJSONClean(id object); RCT_EXTERN id RCTJSONClean(id object);
// Get MD5 hash of a string // Get MD5 hash of a string