diff --git a/.eslintrc b/.eslintrc index cfb22942b448eed15ffd00d3cf0c114eadea86f0..fd6debbbe6a11c6cd3a9a3eec705f77c35543b42 100644 --- a/.eslintrc +++ b/.eslintrc @@ -2,7 +2,7 @@ "parser": "babel-eslint", "env": { "node": true, - "mocha": true, + "jest": true, "es6": true }, "extends": "eslint:recommended", @@ -18,7 +18,7 @@ "no-trailing-spaces": "error", "quotes": [ "error", - "double" + "single" ], "semi": [ "error", diff --git a/.gitignore b/.gitignore index a00cc16074dcbe47747f5bcac7f019307b6bf19a..19c3c51315ad30e9b678e2737f90241f1f394f14 100644 --- a/.gitignore +++ b/.gitignore @@ -178,3 +178,7 @@ jspm_packages # Optional REPL history .node_repl_history + +coverage/ + +package-lock.json \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 0fe294a6fbff7eacedb9883c44a1b8d49a734a20..45555037ee11931d2b338a3f2d39649732c224a8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,3 @@ language: node_js node_js: - - "7" + - "10" diff --git a/RNNotifications/RCTConvert+RNNotifications.h b/RNNotifications/RCTConvert+RNNotifications.h new file mode 100644 index 0000000000000000000000000000000000000000..8b759d535dce1ad28214e229fc466b86e08ce754 --- /dev/null +++ b/RNNotifications/RCTConvert+RNNotifications.h @@ -0,0 +1,22 @@ +#import "RCTConvert.h" +@import UserNotifications; + +@interface RCTConvert (UIMutableUserNotificationAction) ++ (UIMutableUserNotificationAction *)UIMutableUserNotificationAction:(id)json; +@end + +@interface RCTConvert (UNMutableUserNotificationCategory) ++ (UNNotificationCategory *)UNMutableUserNotificationCategory:(id)json; +@end + +@interface RCTConvert (UNNotificationRequest) ++ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSString*)notificationId; +@end + +@interface RCTConvert (UNNotification) ++ (NSDictionary *)UNNotificationPayload:(UNNotification *)notification; +@end + +@interface RCTConvert (UNNotificationPresentationOptions) ++ (UNNotificationPresentationOptions)UNNotificationPresentationOptions:(id)json; +@end diff --git a/RNNotifications/RCTConvert+RNNotifications.m b/RNNotifications/RCTConvert+RNNotifications.m new file mode 100644 index 0000000000000000000000000000000000000000..3c5f7c9d42bf09a0b31bfbaceb7d8dbe795a556c --- /dev/null +++ b/RNNotifications/RCTConvert+RNNotifications.m @@ -0,0 +1,144 @@ +#import "RCTConvert+RNNotifications.h" + + +@implementation RCTConvert (UIUserNotificationActivationMode) +RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{ + @"foreground": @(UIUserNotificationActivationModeForeground), + @"background": @(UIUserNotificationActivationModeBackground) + }), UIUserNotificationActivationModeForeground, integerValue) +@end + +@implementation RCTConvert (UNNotificationActionOptions) + ++ (UNNotificationActionOptions)UNUserNotificationActionOptions:(id)json { + UNNotificationActionOptions options = UNNotificationActionOptionNone; + if ([json[@"activationMode"] isEqualToString:@"foreground"]) { + options = options | UNNotificationActionOptionForeground; + } + if ([RCTConvert BOOL:json[@"authenticationRequired"]]) { + options = options | UNNotificationActionOptionAuthenticationRequired; + } + if ([RCTConvert BOOL:json[@"destructive"]]) { + options = options | UNNotificationActionOptionDestructive; + } + + return options; +} + +@end + +@implementation RCTConvert (UNMutableUserNotificationAction) + ++ (UNNotificationAction *)UNMutableUserNotificationAction:(id)json { + UNNotificationAction* action; + NSDictionary *details = [self NSDictionary:json]; + + if (details[@"textInput"]) { + action = [UNTextInputNotificationAction actionWithIdentifier:details[@"identifier"] title:details[@"title"] options:[RCTConvert UNUserNotificationActionOptions:details] textInputButtonTitle:details[@"textInput"][@"buttonTitle"] textInputPlaceholder:details[@"textInput"][@"placeholder"]]; + } else { + action = [UNNotificationAction actionWithIdentifier:details[@"identifier"] title:details[@"title"] options:[RCTConvert UNUserNotificationActionOptions:details]]; + } + + return action; +} + +@end + +@implementation RCTConvert (UNMutableUserNotificationCategory) + ++ (UNNotificationCategory *)UNMutableUserNotificationCategory:(id)json { + NSDictionary *details = [self NSDictionary:json]; + + NSMutableArray* actions = [NSMutableArray new]; + for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) { + [actions addObject:[RCTConvert UNMutableUserNotificationAction:actionJson]]; + } + + UNNotificationCategory* category = [UNNotificationCategory categoryWithIdentifier:details[@"identifier"] actions:actions intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]; + + return category; +} + +@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[@"body"]]; + content.title = [RCTConvert NSString:details[@"title"]]; + content.sound = [RCTConvert NSString:details[@"sound"]] + ? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"sound"]]] + : [UNNotificationSound defaultSound]; + if ([RCTConvert BOOL:details[@"silent"]]) { + content.sound = nil; + } + 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 + +@implementation RCTConvert (UNNotification) + ++ (NSDictionary *)UNNotificationPayload:(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[@"date"] = dateString; + } + + formattedNotification[@"title"] = RCTNullIfNil(content.title); + formattedNotification[@"body"] = RCTNullIfNil(content.body); + formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier); + formattedNotification[@"thread"] = RCTNullIfNil(content.threadIdentifier); + [formattedNotification addEntriesFromDictionary:RCTNullIfNil(RCTJSONClean(content.userInfo))]; + + return formattedNotification; +} + +@end + +@implementation RCTConvert (UNNotificationPresentationOptions) + ++ (UNNotificationPresentationOptions)UNNotificationPresentationOptions:(id)json { + UNNotificationPresentationOptions options = UNNotificationPresentationOptionNone; + if ([RCTConvert BOOL:json[@"alert"]]) { + options = options | UNNotificationPresentationOptionAlert; + } + if ([RCTConvert BOOL:json[@"badge"]]) { + options = options | UNNotificationPresentationOptionBadge; + } + if ([RCTConvert BOOL:json[@"sound"]]) { + options = options | UNNotificationPresentationOptionSound; + } + + return options; +} + +@end diff --git a/RNNotifications/RNBridgeModule.h b/RNNotifications/RNBridgeModule.h new file mode 100644 index 0000000000000000000000000000000000000000..6e018e37063d502f5e217291f85f7a6a6eea731d --- /dev/null +++ b/RNNotifications/RNBridgeModule.h @@ -0,0 +1,7 @@ +#import +#import + +@interface RNBridgeModule : NSObject + +@end + diff --git a/RNNotifications/RNBridgeModule.m b/RNNotifications/RNBridgeModule.m new file mode 100644 index 0000000000000000000000000000000000000000..fd5653b77a9703f01f9366ece6d7a3acd1f76497 --- /dev/null +++ b/RNNotifications/RNBridgeModule.m @@ -0,0 +1,104 @@ +#import "RNBridgeModule.h" +#import "RNCommandsHandler.h" +#import "RCTConvert+RNNotifications.h" +#import "RNNotificationsStore.h" +#import + +@implementation RNBridgeModule { + RNCommandsHandler* _commandsHandler; +} + +@synthesize bridge = _bridge; + +RCT_EXPORT_MODULE(); + +- (instancetype)init { + self = [super init]; + _commandsHandler = [[RNCommandsHandler alloc] init]; + return self; +} + ++ (BOOL)requiresMainQueueSetup { + return YES; +} + +- (void)setBridge:(RCTBridge *)bridge { + _bridge = bridge; + if ([_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]) { + [[RNNotificationsStore sharedInstance] setInitialNotification:[_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]]; + } +} + +#pragma mark - JS interface + +RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json) { + [_commandsHandler requestPermissionsWithCategories:json]; +} + +RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [_commandsHandler getInitialNotification:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(finishHandlingAction:(NSString *)completionKey) { + [_commandsHandler finishHandlingAction:completionKey]; +} + +RCT_EXPORT_METHOD(finishPresentingNotification:(NSString *)completionKey presentingOptions:(NSDictionary *)presentingOptions) { + [_commandsHandler finishPresentingNotification:completionKey presentingOptions:presentingOptions]; +} + +RCT_EXPORT_METHOD(abandonPermissions) { + [_commandsHandler abandonPermissions]; +} + +RCT_EXPORT_METHOD(registerPushKit) { + [_commandsHandler registerPushKit]; +} + +RCT_EXPORT_METHOD(getBadgesCount:(RCTResponseSenderBlock)callback) { + [_commandsHandler getBadgesCount:callback]; +} + +RCT_EXPORT_METHOD(setBadgesCount:(int)count) { + [_commandsHandler setBadgesCount:count]; +} + +RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId) { + [_commandsHandler sendLocalNotification:notification withId:notificationId]; +} + +RCT_EXPORT_METHOD(cancelLocalNotification:(NSString *)notificationId) { + [_commandsHandler cancelLocalNotification:notificationId]; +} + +RCT_EXPORT_METHOD(cancelAllLocalNotifications) { + [_commandsHandler cancelAllLocalNotifications]; +} + +RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [_commandsHandler isRegisteredForRemoteNotifications:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(checkPermissions:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) { + [_commandsHandler checkPermissions:resolve reject:reject]; +} + +#if !TARGET_OS_TV + +RCT_EXPORT_METHOD(removeAllDeliveredNotifications) { + [_commandsHandler removeAllDeliveredNotifications]; +} + +RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray *)identifiers) { + [_commandsHandler removeDeliveredNotifications:identifiers]; +} + +RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback) { + [_commandsHandler getDeliveredNotifications:callback]; +} + +#endif + +@end + diff --git a/RNNotifications/RNCommandsHandler.h b/RNNotifications/RNCommandsHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..244726803494ce4f4408d0f297b11de21bab492a --- /dev/null +++ b/RNNotifications/RNCommandsHandler.h @@ -0,0 +1,43 @@ +#import +#import +#import "RNNotificationCenter.h" + +@interface RNCommandsHandler : NSObject + +- (instancetype)init; + +- (void)requestPermissionsWithCategories:(NSArray *)json; + +- (void)getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject; + +- (void)finishHandlingAction:(NSString *)completionKey; + +- (void)finishPresentingNotification:(NSString *)completionKey presentingOptions:(NSDictionary *)presentingOptions; + +- (void)abandonPermissions; + +- (void)registerPushKit; + +- (void)getBadgesCount:(RCTResponseSenderBlock)callback; + +- (void)setBadgesCount:(int)count; + +- (void)sendLocalNotification:(NSDictionary *)notification withId:(NSString *)notificationId; + +- (void)cancelLocalNotification:(NSString *)notificationId; + +- (void)cancelAllLocalNotifications; + +- (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +- (void)checkPermissions:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject; + +- (void)removeAllDeliveredNotifications; + +- (void)removeDeliveredNotifications:(NSArray *)identifiers; + +- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback; + +@end diff --git a/RNNotifications/RNCommandsHandler.m b/RNNotifications/RNCommandsHandler.m new file mode 100644 index 0000000000000000000000000000000000000000..4daf611b36f032e60e91f3d56bc81b2f08704dce --- /dev/null +++ b/RNNotifications/RNCommandsHandler.m @@ -0,0 +1,81 @@ +#import "RNCommandsHandler.h" +#import "RNNotifications.h" +#import "RNNotificationsStore.h" +#import "RCTConvert+RNNotifications.h" + +@implementation RNCommandsHandler { + RNNotificationCenter* _notificationCenter; +} + +- (instancetype)init { + self = [super init]; + _notificationCenter = [RNNotificationCenter new]; + return self; +} + +- (void)requestPermissionsWithCategories:(NSArray *)json { + [_notificationCenter requestPermissionsWithCategories:json]; +} + +- (void)getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + resolve([[RNNotificationsStore sharedInstance] initialNotification]); +} + +- (void)finishHandlingAction:(NSString *)completionKey { + [[RNNotificationsStore sharedInstance] completeAction:completionKey]; +} + +- (void)finishPresentingNotification:(NSString *)completionKey presentingOptions:(NSDictionary *)presentingOptions { + [[RNNotificationsStore sharedInstance] completePresentation:completionKey withPresentationOptions:[RCTConvert UNNotificationPresentationOptions:presentingOptions]]; +} + +- (void)abandonPermissions { + [[UIApplication sharedApplication] unregisterForRemoteNotifications]; +} + +- (void)registerPushKit { + [RNNotifications startMonitorPushKitNotifications]; +} + +- (void)getBadgesCount:(RCTResponseSenderBlock)callback { + NSInteger count = [UIApplication sharedApplication].applicationIconBadgeNumber; + callback(@[ [NSNumber numberWithInteger:count] ]); +} + +- (void)setBadgesCount:(int)count { + [[UIApplication sharedApplication] setApplicationIconBadgeNumber:count]; +} + +- (void)sendLocalNotification:(NSDictionary *)notification withId:(NSString *)notificationId { + [_notificationCenter sendLocalNotification:notification withId:notificationId]; +} + +- (void)cancelLocalNotification:(NSString *)notificationId { + [_notificationCenter cancelLocalNotification:notificationId]; +} + +- (void)cancelAllLocalNotifications { + [_notificationCenter cancelAllLocalNotifications]; +} + +- (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [_notificationCenter isRegisteredForRemoteNotifications:resolve]; +} + +- (void)checkPermissions:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [_notificationCenter checkPermissions:resolve]; +} + +- (void)removeAllDeliveredNotifications { + [_notificationCenter removeAllDeliveredNotifications]; +} + +- (void)removeDeliveredNotifications:(NSArray *)identifiers { + [_notificationCenter removeDeliveredNotifications:identifiers]; +} + +- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback { + [_notificationCenter getDeliveredNotifications:callback]; +} + +@end diff --git a/RNNotifications/RNEventEmitter.h b/RNNotifications/RNEventEmitter.h new file mode 100644 index 0000000000000000000000000000000000000000..011477d98832e7316f5d3b775e4399c2a9ad7ed5 --- /dev/null +++ b/RNNotifications/RNEventEmitter.h @@ -0,0 +1,15 @@ +#import + +static NSString* const RNRegistered = @"remoteNotificationsRegistered"; +static NSString* const RNRegistrationFailed = @"remoteNotificationsRegistrationFailed"; +static NSString* const RNPushKitRegistered = @"pushKitRegistered"; +static NSString* const RNNotificationReceivedForeground = @"notificationReceivedForeground"; +static NSString* const RNNotificationOpened = @"notificationOpened"; +static NSString* const RNPushKitNotificationReceived = @"pushKitNotificationReceived"; + + +@interface RNEventEmitter : RCTEventEmitter + ++ (void)sendEvent:(NSString *)event body:(NSDictionary *)body; + +@end diff --git a/RNNotifications/RNEventEmitter.m b/RNNotifications/RNEventEmitter.m new file mode 100644 index 0000000000000000000000000000000000000000..c5764b1a7fc41b4a3f57a3d6ea59cb658fcc29d2 --- /dev/null +++ b/RNNotifications/RNEventEmitter.m @@ -0,0 +1,56 @@ +#import "RNEventEmitter.h" + +@implementation RNEventEmitter + +RCT_EXPORT_MODULE(); + +-(NSArray *)supportedEvents { + return @[RNRegistered, + RNRegistrationFailed, + RNPushKitRegistered, + RNNotificationReceivedForeground, + RNNotificationOpened, + RNPushKitNotificationReceived]; +} + +- (instancetype)init { + self = [super init]; + for (NSString *event in [self supportedEvents]) { + [self addListener:event]; + } + return self; +} + ++ (BOOL)requiresMainQueueSetup { + return YES; +} + +# pragma mark public + ++ (void)sendEvent:(NSString *)event body:(NSDictionary *)body { + [[NSNotificationCenter defaultCenter] postNotificationName:event + object:self + userInfo:body]; +} + +# pragma mark private + +- (void)startObserving { + for (NSString *event in [self supportedEvents]) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleNotification:) + name:event + object:nil]; + } +} + +- (void)stopObserving { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)handleNotification:(NSNotification *)notification { + [self sendEventWithName:notification.name body:notification.userInfo]; +} + + +@end diff --git a/RNNotifications/RNNotificationCenter.h b/RNNotifications/RNNotificationCenter.h new file mode 100644 index 0000000000000000000000000000000000000000..472ef2eee561fdd1a2a367cd530ead586f9dbb21 --- /dev/null +++ b/RNNotifications/RNNotificationCenter.h @@ -0,0 +1,25 @@ +#import +#import +@import UserNotifications; + +@interface RNNotificationCenter : NSObject + +- (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve; + +- (void)requestPermissionsWithCategories:(NSArray *)json; + +- (void)checkPermissions:(RCTPromiseResolveBlock)resolve; + +- (void)sendLocalNotification:(NSDictionary *)notification withId:(NSString *)notificationId; + +- (void)cancelLocalNotification:(NSString *)notificationId; + +- (void)removeAllDeliveredNotifications; + +- (void)removeDeliveredNotifications:(NSArray *)identifiers; + +- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback; + +- (void)cancelAllLocalNotifications; + +@end diff --git a/RNNotifications/RNNotificationCenter.m b/RNNotifications/RNNotificationCenter.m new file mode 100644 index 0000000000000000000000000000000000000000..af4c11053d9bdb78dda9415c40fe1c70340a3252 --- /dev/null +++ b/RNNotifications/RNNotificationCenter.m @@ -0,0 +1,92 @@ +#import "RNNotificationCenter.h" +#import "RCTConvert+RNNotifications.h" + +@implementation RNNotificationCenter + +- (void)requestPermissionsWithCategories:(NSArray *)json { + NSMutableSet* categories = nil; + + if ([json count] > 0) { + categories = [NSMutableSet new]; + for (NSDictionary* categoryJson in json) { + [categories addObject:[RCTConvert UNMutableUserNotificationCategory:categoryJson]]; + } + } + [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:categories]; + UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert); + [UNUserNotificationCenter.currentNotificationCenter requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { + if (error) { + + } else { + if (granted) { + [UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { + if (settings.authorizationStatus == UNAuthorizationStatusAuthorized) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication] registerForRemoteNotifications]; + }); + } + }]; + } else { + + } + } + }]; +} + +- (void)sendLocalNotification:(NSDictionary *)notification withId:(NSString *)notificationId { + UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId]; + [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil]; +} + +- (void)cancelLocalNotification:(NSString *)notificationId { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center removePendingNotificationRequestsWithIdentifiers:@[notificationId]]; +} + +- (void)removeAllDeliveredNotifications { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center removeAllDeliveredNotifications]; +} + +- (void)removeDeliveredNotifications:(NSArray *)identifiers { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center removeDeliveredNotificationsWithIdentifiers:identifiers]; +} + +- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback { + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center getDeliveredNotificationsWithCompletionHandler:^(NSArray * _Nonnull notifications) { + NSMutableArray *formattedNotifications = [NSMutableArray new]; + + for (UNNotification *notification in notifications) { + [formattedNotifications addObject:[RCTConvert UNNotificationPayload:notification]]; + } + callback(@[formattedNotifications]); + }]; +} + +- (void)cancelAllLocalNotifications { + [[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests]; +} + +- (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve { + [[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { + if (settings.alertSetting == UNNotificationSettingEnabled || settings.soundSetting == UNNotificationSettingEnabled || settings.badgeSetting == UNNotificationSettingEnabled) { + resolve(@(YES)); + } else { + resolve(@(NO)); + } + }]; +} + +- (void)checkPermissions:(RCTPromiseResolveBlock)resolve { + [[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { + resolve(@{ + @"badge": @(settings.badgeSetting == UNNotificationSettingEnabled), + @"sound": @(settings.soundSetting == UNNotificationSettingEnabled), + @"alert": @(settings.alertSetting == UNNotificationSettingEnabled), + }); + }]; +} + +@end diff --git a/RNNotifications/RNNotificationCenterListener.h b/RNNotifications/RNNotificationCenterListener.h new file mode 100644 index 0000000000000000000000000000000000000000..77a67dd3bb1983004939b5e155fb85f62de0ce9a --- /dev/null +++ b/RNNotifications/RNNotificationCenterListener.h @@ -0,0 +1,9 @@ +#import +@import UserNotifications; +#import "RNNotificationEventHandler.h" + +@interface RNNotificationCenterListener : NSObject + +- (instancetype)initWithNotificationEventHandler:(RNNotificationEventHandler *)notificationEventHandler; + +@end diff --git a/RNNotifications/RNNotificationCenterListener.m b/RNNotifications/RNNotificationCenterListener.m new file mode 100644 index 0000000000000000000000000000000000000000..97243b27661085fab207094339b48d47af6b5898 --- /dev/null +++ b/RNNotifications/RNNotificationCenterListener.m @@ -0,0 +1,24 @@ +#import "RNNotificationCenterListener.h" +#import "RCTConvert+RNNotifications.h" + +@implementation RNNotificationCenterListener { + RNNotificationEventHandler* _notificationEventHandler; +} + +- (instancetype)initWithNotificationEventHandler:(RNNotificationEventHandler *)notificationEventHandler { + self = [super init]; + _notificationEventHandler = notificationEventHandler; + [[UNUserNotificationCenter currentNotificationCenter] setDelegate:self]; + + return self; +} + +- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { + [_notificationEventHandler didReceiveForegroundNotification:notification withCompletionHandler:completionHandler]; +} + +- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { + [_notificationEventHandler didReceiveNotificationResponse:response completionHandler:completionHandler]; +} + +@end diff --git a/RNNotifications/RNNotificationEventHandler.h b/RNNotifications/RNNotificationEventHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..413e0cd4afdbf3eb18fd9dbd73af5211793c391f --- /dev/null +++ b/RNNotifications/RNNotificationEventHandler.h @@ -0,0 +1,16 @@ +#import +@import UserNotifications; +#import "RNNotificationsStore.h" +#import "RNEventEmitter.h" + +@interface RNNotificationEventHandler : NSObject + +- (instancetype)initWithStore:(RNNotificationsStore *)store; + +- (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken; +- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; + +- (void)didReceiveForegroundNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler; +- (void)didReceiveNotificationResponse:(UNNotificationResponse *)notificationResponse completionHandler:(void (^)())completionHandler; + +@end diff --git a/RNNotifications/RNNotificationEventHandler.m b/RNNotifications/RNNotificationEventHandler.m new file mode 100644 index 0000000000000000000000000000000000000000..7dac47e566a8ae0dbabb69db6193357c3a8d598b --- /dev/null +++ b/RNNotifications/RNNotificationEventHandler.m @@ -0,0 +1,36 @@ +#import "RNNotificationEventHandler.h" +#import "RNEventEmitter.h" +#import "RNNotificationUtils.h" +#import "RCTConvert+RNNotifications.h" +#import "RNNotificationParser.h" + +@implementation RNNotificationEventHandler { + RNNotificationsStore* _store; +} + +- (instancetype)initWithStore:(RNNotificationsStore *)store { + self = [super init]; + _store = store; + return self; +} + +- (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken { + NSString *tokenRepresentation = [deviceToken isKindOfClass:[NSString class]] ? deviceToken : [RNNotificationUtils deviceTokenToString:deviceToken]; + [RNEventEmitter sendEvent:RNRegistered body:@{@"deviceToken": tokenRepresentation}]; +} + +- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + [RNEventEmitter sendEvent:RNRegistrationFailed body:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}]; +} + +- (void)didReceiveForegroundNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { + [_store setPresentationCompletionHandler:completionHandler withCompletionKey:notification.request.identifier]; + [RNEventEmitter sendEvent:RNNotificationReceivedForeground body:[RNNotificationParser parseNotification:notification]]; +} + +- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)())completionHandler { + [_store setActionCompletionHandler:completionHandler withCompletionKey:response.notification.request.identifier]; + [RNEventEmitter sendEvent:RNNotificationOpened body:[RNNotificationParser parseNotificationResponse:response]]; +} + +@end diff --git a/RNNotifications/RNNotificationParser.h b/RNNotifications/RNNotificationParser.h new file mode 100644 index 0000000000000000000000000000000000000000..6397d20ce71834ab6041eafaf7180f2e0fb2e0b9 --- /dev/null +++ b/RNNotifications/RNNotificationParser.h @@ -0,0 +1,9 @@ +#import +@import UserNotifications; + +@interface RNNotificationParser : NSObject + ++ (NSDictionary *)parseNotificationResponse:(UNNotificationResponse *)response; ++ (NSDictionary *)parseNotification:(UNNotification *)notification; + +@end diff --git a/RNNotifications/RNNotificationParser.m b/RNNotifications/RNNotificationParser.m new file mode 100644 index 0000000000000000000000000000000000000000..9a2815a2a948b682eec4c824bc6ae863bd920db2 --- /dev/null +++ b/RNNotifications/RNNotificationParser.m @@ -0,0 +1,31 @@ +#import "RNNotificationParser.h" +#import "RCTConvert+RNNotifications.h" + +@implementation RNNotificationParser + ++ (NSDictionary *)parseNotification:(UNNotification *)notification { + NSDictionary* notificationDict = @{@"identifier": notification.request.identifier, + @"payload": [RCTConvert UNNotificationPayload:notification] + }; + + return notificationDict; +} + ++ (NSDictionary *)parseNotificationResponse:(UNNotificationResponse *)response { + NSDictionary* responseDict = @{@"payload": [RCTConvert UNNotificationPayload:response.notification], @"identifier": response.notification.request.identifier, @"action": [self parseNotificationResponseAction:response]}; + + return responseDict; +} + ++ (NSDictionary *)parseNotificationResponseAction:(UNNotificationResponse *)response { + NSMutableDictionary* responseAction = [NSMutableDictionary dictionaryWithDictionary:@{@"identifier": response.actionIdentifier}]; + + NSString* responseText = [response isKindOfClass:[UNTextInputNotificationResponse class]] ? ((UNTextInputNotificationResponse *)response).userText : nil; + if (responseText) { + [responseAction setObject:responseText forKey:@"text"]; + } + + return responseAction; +} + +@end diff --git a/RNNotifications/RNNotificationUtils.h b/RNNotifications/RNNotificationUtils.h new file mode 100644 index 0000000000000000000000000000000000000000..01abacbbe8d63647534dc1503fd2171ae48b53f4 --- /dev/null +++ b/RNNotifications/RNNotificationUtils.h @@ -0,0 +1,7 @@ +#import + +@interface RNNotificationUtils : NSObject + ++ (NSString *)deviceTokenToString:(NSData *)deviceToken; + +@end diff --git a/RNNotifications/RNNotificationUtils.m b/RNNotifications/RNNotificationUtils.m new file mode 100644 index 0000000000000000000000000000000000000000..c7cda141cd919f91288b4cab074689f9663f75e0 --- /dev/null +++ b/RNNotifications/RNNotificationUtils.m @@ -0,0 +1,16 @@ +#import "RNNotificationUtils.h" + +@implementation RNNotificationUtils + ++ (NSString *)deviceTokenToString:(NSData *)deviceToken { + NSMutableString *result = [NSMutableString string]; + NSUInteger deviceTokenLength = deviceToken.length; + const unsigned char *bytes = deviceToken.bytes; + for (NSUInteger i = 0; i < deviceTokenLength; i++) { + [result appendFormat:@"%02x", bytes[i]]; + } + + return [result copy]; +} + +@end diff --git a/RNNotifications/RNNotifications.h b/RNNotifications/RNNotifications.h index b1c34de28bfc3c481d70fff12c09491ed8f53975..2e277393ee9bde13133fb0beb5ac7a8e1b09d6b8 100644 --- a/RNNotifications/RNNotifications.h +++ b/RNNotifications/RNNotifications.h @@ -1,19 +1,15 @@ @import UIKit; - -#import #import +@import UserNotifications; -@interface RNNotifications : NSObject +@interface RNNotifications : NSObject -+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken; -+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; -+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; -+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type; ++ (instancetype)sharedInstance; -+ (void)didReceiveRemoteNotification:(NSDictionary *)notification; -+ (void)didReceiveLocalNotification:(UILocalNotification *)notification; ++ (void)startMonitorNotifications; ++ (void)startMonitorPushKitNotifications; -+ (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler; -+ (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler; ++ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken; ++ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; @end diff --git a/RNNotifications/RNNotifications.m b/RNNotifications/RNNotifications.m index 80bb3be71ea269098befe95bb97d910c932a9a12..714a62ac69fccf47dc4898db3ff26a0a9db5b142 100644 --- a/RNNotifications/RNNotifications.m +++ b/RNNotifications/RNNotifications.m @@ -2,691 +2,65 @@ #import #import #import -#import #import "RNNotifications.h" -#import -#import -#import "RNNotificationsBridgeQueue.h" -#import +#import "RNNotificationCenterListener.h" +#import "RNPushKit.h" -#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"; - -NSString* const RNNotificationsRegistered = @"RNNotificationsRegistered"; -NSString* const RNNotificationsRegistrationFailed = @"RNNotificationsRegistrationFailed"; -NSString* const RNPushKitRegistered = @"RNPushKitRegistered"; -NSString* const RNNotificationReceivedForeground = @"RNNotificationReceivedForeground"; -NSString* const RNNotificationReceivedBackground = @"RNNotificationReceivedBackground"; -NSString* const RNNotificationOpened = @"RNNotificationOpened"; -NSString* const RNNotificationActionTriggered = @"RNNotificationActionTriggered"; - -/* - * Converters for Interactive Notifications - */ -@implementation RCTConvert (UIUserNotificationActivationMode) -RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{ - @"foreground": @(UIUserNotificationActivationModeForeground), - @"background": @(UIUserNotificationActivationModeBackground) - }), UIUserNotificationActivationModeForeground, integerValue) -@end - -@implementation RCTConvert (UIUserNotificationActionContext) -RCT_ENUM_CONVERTER(UIUserNotificationActionContext, (@{ - @"default": @(UIUserNotificationActionContextDefault), - @"minimal": @(UIUserNotificationActionContextMinimal) - }), UIUserNotificationActionContextDefault, integerValue) -@end - -@implementation RCTConvert (UIUserNotificationActionBehavior) -/* iOS 9 only */ -RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{ - @"default": @(UIUserNotificationActionBehaviorDefault), - @"textInput": @(UIUserNotificationActionBehaviorTextInput) - }), UIUserNotificationActionBehaviorDefault, integerValue) -@end - -@implementation RCTConvert (UIMutableUserNotificationAction) -+ (UIMutableUserNotificationAction *)UIMutableUserNotificationAction:(id)json -{ - NSDictionary *details = [self NSDictionary:json]; - - UIMutableUserNotificationAction* action =[UIMutableUserNotificationAction new]; - action.activationMode = [RCTConvert UIUserNotificationActivationMode:details[@"activationMode"]]; - action.behavior = [RCTConvert UIUserNotificationActionBehavior:details[@"behavior"]]; - action.authenticationRequired = [RCTConvert BOOL:details[@"authenticationRequired"]]; - action.destructive = [RCTConvert BOOL:details[@"destructive"]]; - action.title = [RCTConvert NSString:details[@"title"]]; - action.identifier = [RCTConvert NSString:details[@"identifier"]]; - - return action; +@implementation RNNotifications { + RNPushKit* _pushKit; + RNNotificationCenterListener* _notificationCenterListener; + RNNotificationEventHandler* _notificationEventHandler; + RNPushKitEventHandler* _pushKitEventHandler; + RNEventEmitter* _eventEmitter; } -@end - -@implementation RCTConvert (UIMutableUserNotificationCategory) -+ (UIMutableUserNotificationCategory *)UIMutableUserNotificationCategory:(id)json -{ - NSDictionary *details = [self NSDictionary:json]; - - UIMutableUserNotificationCategory* category = [UIMutableUserNotificationCategory new]; - category.identifier = details[@"identifier"]; - - // category actions - NSMutableArray* actions = [NSMutableArray new]; - for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) { - [actions addObject:[RCTConvert UIMutableUserNotificationAction:actionJson]]; - } - [category setActions:actions forContext:[RCTConvert UIUserNotificationActionContext:details[@"context"]]]; - - return category; +- (instancetype)init { + self = [super init]; + _notificationEventHandler = [[RNNotificationEventHandler alloc] initWithStore:[RNNotificationsStore new]]; + return self; } -@end - -@implementation RCTConvert (UILocalNotification) -+ (UILocalNotification *)UILocalNotification:(id)json -{ - NSDictionary *details = [self NSDictionary:json]; - UILocalNotification* notification = [UILocalNotification new]; - notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]]; - notification.alertBody = [RCTConvert NSString:details[@"alertBody"]]; - notification.alertTitle = [RCTConvert NSString:details[@"alertTitle"]]; - notification.alertAction = [RCTConvert NSString:details[@"alertAction"]]; - notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName; - if ([RCTConvert BOOL:details[@"silent"]]) { - notification.soundName = nil; - } - notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{}; - notification.category = [RCTConvert NSString:details[@"category"]]; - - return notification; ++ (instancetype)sharedInstance { + static RNNotifications *sharedInstance = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + sharedInstance = [[RNNotifications alloc] init]; + }); + return sharedInstance; } -@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]; - if ([RCTConvert BOOL:details[@"silent"]]) { - content.sound = nil; - } - 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]; ++ (void)startMonitorNotifications { + [[self sharedInstance] startMonitorNotifications]; } -@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; ++ (void)startMonitorPushKitNotifications { + [[self sharedInstance] startMonitorPushKitNotifications]; } -@implementation RNNotifications - -RCT_EXPORT_MODULE() - -@synthesize bridge = _bridge; - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; -} - -+ (BOOL)requiresMainQueueSetup { - return YES; -} - -- (void)setBridge:(RCTBridge *)bridge -{ - _bridge = bridge; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationsRegistered:) - name:RNNotificationsRegistered - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationsRegistrationFailed:) - name:RNNotificationsRegistrationFailed - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handlePushKitRegistered:) - name:RNPushKitRegistered - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationReceivedForeground:) - name:RNNotificationReceivedForeground - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationReceivedBackground:) - name:RNNotificationReceivedBackground - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationOpened:) - name:RNNotificationOpened - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleNotificationActionTriggered:) - name:RNNotificationActionTriggered - object:nil]; - - [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; - UILocalNotification *localNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey]; - [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = localNotification ? localNotification.userInfo : nil; -} - -/* - * Public Methods - */ -+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings -{ - if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) { - [[UIApplication sharedApplication] registerForRemoteNotifications]; - } -} - -+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken -{ - NSString *tokenRepresentation = [deviceToken isKindOfClass:[NSString class]] ? deviceToken : [self deviceTokenToString:deviceToken]; - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistered - object:self - userInfo:@{@"deviceToken": tokenRepresentation}]; ++ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken { + [[self sharedInstance] didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } + (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistrationFailed - object:self - userInfo:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}]; -} - -+ (void)didReceiveRemoteNotification:(NSDictionary *)notification -{ - UIApplicationState state = [UIApplication sharedApplication].applicationState; - - if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) { - // JS thread is ready, push the notification to the bridge - - if (state == UIApplicationStateActive) { - // Notification received foreground - [self didReceiveNotificationOnForegroundState:notification]; - } else if (state == UIApplicationStateInactive) { - // Notification opened - [self didNotificationOpen:notification]; - } else { - // Notification received background - [self didReceiveNotificationOnBackgroundState:notification]; - } - } else { - // JS thread is not ready - store it in the native notifications queue - [[RNNotificationsBridgeQueue sharedInstance] postNotification:notification]; - } -} - -+ (void)didReceiveLocalNotification:(UILocalNotification *)notification -{ - UIApplicationState state = [UIApplication sharedApplication].applicationState; - - NSMutableDictionary* newUserInfo = notification.userInfo.mutableCopy; - [newUserInfo removeObjectForKey:@"__id"]; - notification.userInfo = newUserInfo; - - if (state == UIApplicationStateActive) { - [self didReceiveNotificationOnForegroundState:notification.userInfo]; - } else if (state == UIApplicationStateInactive) { - NSString* notificationId = [notification.userInfo objectForKey:@"notificationId"]; - if (notificationId) { - [self clearNotificationFromNotificationsCenter:notificationId]; - } - [self didNotificationOpen:notification.userInfo]; - } -} - -+ (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:notification.userInfo completionHandler:completionHandler]; -} - -+ (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:userInfo completionHandler:completionHandler]; -} - -/* - * Notification handlers - */ -+ (void)didReceiveNotificationOnForegroundState:(NSDictionary *)notification -{ - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedForeground - object:self - userInfo:notification]; -} - -+ (void)didReceiveNotificationOnBackgroundState:(NSDictionary *)notification -{ - NSDictionary* managedAps = [notification objectForKey:@"managedAps"]; - NSDictionary* alert = [managedAps objectForKey:@"alert"]; - NSString* action = [managedAps objectForKey:@"action"]; - NSString* notificationId = [managedAps objectForKey:@"notificationId"]; - - if (action) { - // create or delete notification - if ([action isEqualToString: RNNotificationCreateAction] - && notificationId - && alert) { - [self dispatchLocalNotificationFromNotification:notification]; - - } else if ([action isEqualToString: RNNotificationClearAction] && notificationId) { - [self clearNotificationFromNotificationsCenter:notificationId]; - } - } - - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedBackground - object:self - userInfo:notification]; -} - -+ (void)didNotificationOpen:(NSDictionary *)notification -{ - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationOpened - object:self - userInfo:notification]; -} - -/* - * 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: RNNotificationCreateAction] - && 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); - } -} - -+ (void)clearNotificationFromNotificationsCenter:(NSString *)notificationId -{ - 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; - } -} - -+ (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 = deviceToken.bytes; - for (NSUInteger i = 0; i < deviceTokenLength; i++) { - [result appendFormat:@"%02x", bytes[i]]; - } - - return [result copy]; -} - -+ (void)requestPermissionsWithCategories:(NSMutableSet *)categories -{ - UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert); - UIUserNotificationSettings* settings = [UIUserNotificationSettings settingsForTypes:types categories:categories]; - - [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; -} - -+ (void)emitNotificationActionForIdentifier:(NSString *)identifier responseInfo:(NSDictionary *)responseInfo userInfo:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler -{ - NSString* completionKey = [NSString stringWithFormat:@"%@.%@", identifier, [NSString stringWithFormat:@"%d", (long)[[NSDate date] timeIntervalSince1970]]]; - NSMutableDictionary* info = [[NSMutableDictionary alloc] initWithDictionary:@{ @"identifier": identifier, @"completionKey": completionKey }]; - - // add text - NSString* text = [responseInfo objectForKey:UIUserNotificationActionResponseTypedTextKey]; - if (text != NULL) { - info[@"text"] = text; - } - - // add notification custom data - if (userInfo != NULL) { - info[@"notification"] = userInfo; - } - - // Emit event to the queue (in order to store the completion handler). if JS thread is ready, post it also to the notification center (to the bridge). - [[RNNotificationsBridgeQueue sharedInstance] postAction:info withCompletionKey:completionKey andCompletionHandler:completionHandler]; - - if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) { - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered - object:self - userInfo:info]; - } -} - -+ (void)registerPushKit -{ - // Create a push registry object - PKPushRegistry* pushKitRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()]; - - // Set the registry delegate to app delegate - pushKitRegistry.delegate = [[UIApplication sharedApplication] delegate]; - pushKitRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; + [[self sharedInstance] didFailToRegisterForRemoteNotificationsWithError:error]; } -+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type -{ - [[NSNotificationCenter defaultCenter] postNotificationName:RNPushKitRegistered - object:self - userInfo:@{@"pushKitToken": [self deviceTokenToString:credentials.token]}]; +- (void)startMonitorNotifications { + _notificationCenterListener = [[RNNotificationCenterListener alloc] initWithNotificationEventHandler:_notificationEventHandler]; } -- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type -{ - [RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload]; +- (void)startMonitorPushKitNotifications { + _pushKitEventHandler = [RNPushKitEventHandler new]; + _pushKit = [[RNPushKit alloc] initWithEventHandler:_pushKitEventHandler]; } -/* - * Javascript events - */ -- (void)handleNotificationsRegistered:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo]; +- (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken { + [_notificationEventHandler didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } -- (void)handleNotificationsRegistrationFailed:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistrationFailed" body:notification.userInfo]; +- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + [_notificationEventHandler didFailToRegisterForRemoteNotificationsWithError:error]; } -- (void)handlePushKitRegistered:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"pushKitRegistered" body:notification.userInfo]; -} - -- (void)handleNotificationReceivedForeground:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedForeground" body:notification.userInfo]; -} - -- (void)handleNotificationReceivedBackground:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedBackground" body:notification.userInfo]; -} - -- (void)handleNotificationOpened:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationOpened" body:notification.userInfo]; -} - -- (void)handleNotificationActionTriggered:(NSNotification *)notification -{ - [_bridge.eventDispatcher sendAppEventWithName:@"notificationActionReceived" body:notification.userInfo]; -} - -/* - * React Native exported methods - */ -RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json) -{ - NSMutableSet* categories = nil; - - if ([json count] > 0) { - categories = [NSMutableSet new]; - for (NSDictionary* categoryJson in json) { - [categories addObject:[RCTConvert UIMutableUserNotificationCategory:categoryJson]]; - } - } - - [RNNotifications requestPermissionsWithCategories:categories]; -} - -RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -{ - NSDictionary * notification = nil; - notification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification ? - [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification : - [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification; - [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil; - [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil; - resolve(notification); -} - -RCT_EXPORT_METHOD(log:(NSString *)message) -{ - NSLog(message); -} - -RCT_EXPORT_METHOD(completionHandler:(NSString *)completionKey) -{ - [[RNNotificationsBridgeQueue sharedInstance] completeAction:completionKey]; -} - -RCT_EXPORT_METHOD(abandonPermissions) -{ - [[UIApplication sharedApplication] unregisterForRemoteNotifications]; -} - -RCT_EXPORT_METHOD(registerPushKit) -{ - [RNNotifications registerPushKit]; -} - -RCT_EXPORT_METHOD(getBadgesCount:(RCTResponseSenderBlock)callback) -{ - NSInteger count = [UIApplication sharedApplication].applicationIconBadgeNumber; - callback(@[ [NSNumber numberWithInteger:count] ]); -} - -RCT_EXPORT_METHOD(setBadgesCount:(int)count) -{ - [[UIApplication sharedApplication] setApplicationIconBadgeNumber:count]; -} - -RCT_EXPORT_METHOD(backgroundTimeRemaining:(RCTResponseSenderBlock)callback) -{ - NSTimeInterval remainingTime = [UIApplication sharedApplication].backgroundTimeRemaining; - callback(@[ [NSNumber numberWithDouble:remainingTime] ]); -} - -RCT_EXPORT_METHOD(consumeBackgroundQueue) -{ - // Mark JS Thread as ready - [RNNotificationsBridgeQueue sharedInstance].jsIsReady = YES; - - // Push actions to JS - [[RNNotificationsBridgeQueue sharedInstance] consumeActionsQueue:^(NSDictionary* action) { - [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered - object:self - userInfo:action]; - }]; - - // Push background notifications to JS - [[RNNotificationsBridgeQueue sharedInstance] consumeNotificationsQueue:^(NSDictionary* notification) { - [RNNotifications didReceiveNotificationOnBackgroundState:notification]; - }]; - - // Push opened local notifications - NSDictionary* openedLocalNotification = [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification; - if (openedLocalNotification) { - [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil; - [RNNotifications didNotificationOpen:openedLocalNotification]; - } - - // Push opened remote notifications - NSDictionary* openedRemoteNotification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification; - if (openedRemoteNotification) { - [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil; - [RNNotifications didNotificationOpen:openedRemoteNotification]; - } -} - -RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId) -{ - if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) { - UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId]; - [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil]; - } else { - 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) -{ - 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]; - } - } - } -} - -RCT_EXPORT_METHOD(cancelAllLocalNotifications) -{ - [RCTSharedApplication() cancelAllLocalNotifications]; -} - -RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -{ - BOOL ans = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != 0; - resolve(@(ans)); -} - -RCT_EXPORT_METHOD(checkPermissions:(RCTPromiseResolveBlock) resolve - reject:(RCTPromiseRejectBlock) reject) { - UIUserNotificationSettings *currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings]; - resolve(@{ - @"badge": @((currentSettings.types & UIUserNotificationTypeBadge) > 0), - @"sound": @((currentSettings.types & UIUserNotificationTypeSound) > 0), - @"alert": @((currentSettings.types & UIUserNotificationTypeAlert) > 0), - }); -} - -#if !TARGET_OS_TV - -RCT_EXPORT_METHOD(removeAllDeliveredNotifications) -{ - if ([UNUserNotificationCenter class]) { - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - [center removeAllDeliveredNotifications]; - } -} - -RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray *)identifiers) -{ - if ([UNUserNotificationCenter class]) { - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - [center removeDeliveredNotificationsWithIdentifiers:identifiers]; - } -} - -RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback) -{ - if ([UNUserNotificationCenter class]) { - UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - [center getDeliveredNotificationsWithCompletionHandler:^(NSArray * _Nonnull notifications) { - NSMutableArray *formattedNotifications = [NSMutableArray new]; - - for (UNNotification *notification in notifications) { - [formattedNotifications addObject:RCTFormatUNNotification(notification)]; - } - callback(@[formattedNotifications]); - }]; - } -} - -#endif !TARGET_OS_TV - @end diff --git a/RNNotifications/RNNotifications.xcodeproj/project.pbxproj b/RNNotifications/RNNotifications.xcodeproj/project.pbxproj index 761eb17d0e7b357ae2f91fcb63cfd9d21e03e78a..efde26e1c3a6685cad0107a805da15a2635839a1 100644 --- a/RNNotifications/RNNotifications.xcodeproj/project.pbxproj +++ b/RNNotifications/RNNotifications.xcodeproj/project.pbxproj @@ -7,10 +7,49 @@ objects = { /* Begin PBXBuildFile section */ - D85B37451CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */; }; + 50351F8F22CD782F000713B3 /* RNEventEmitter.m in Sources */ = {isa = PBXBuildFile; fileRef = 50351F8E22CD782F000713B3 /* RNEventEmitter.m */; }; + 50351F9222CD7DF4000713B3 /* RNBridgeModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 50351F9122CD7DF4000713B3 /* RNBridgeModule.m */; }; + 50351F9522CD7FF1000713B3 /* RCTConvert+RNNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = 50351F9422CD7FF1000713B3 /* RCTConvert+RNNotifications.m */; }; + 50351F9822CD8604000713B3 /* RNCommandsHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 50351F9722CD8604000713B3 /* RNCommandsHandler.m */; }; + 507DCCF522CE3EBD005D4E0B /* RNNotifications.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */; }; + 507DCCF722CE3EF7005D4E0B /* RNBridgeModule.h in Headers */ = {isa = PBXBuildFile; fileRef = 50351F9022CD7DF4000713B3 /* RNBridgeModule.h */; }; + 507DCCF922CE3F04005D4E0B /* RNNotifications.h in Headers */ = {isa = PBXBuildFile; fileRef = D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 507DCCFA22CE3F04005D4E0B /* RNEventEmitter.h in Headers */ = {isa = PBXBuildFile; fileRef = 50351F8D22CD782F000713B3 /* RNEventEmitter.h */; }; + 507DCCFB22CE3F04005D4E0B /* RNCommandsHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 50351F9622CD8604000713B3 /* RNCommandsHandler.h */; }; + 507DCCFC22CE3F04005D4E0B /* RCTConvert+RNNotifications.h in Headers */ = {isa = PBXBuildFile; fileRef = 50351F9322CD7FF1000713B3 /* RCTConvert+RNNotifications.h */; }; + 508CE7CB22D12B2600357815 /* RNNotificationsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE7CA22D12B2600357815 /* RNNotificationsTests.m */; }; + 508CE7CD22D12B2600357815 /* libRNNotifications.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134814201AA4EA6300B7C361 /* libRNNotifications.a */; }; + 508CE7D522D12CCA00357815 /* RNNotificationEventHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE7D322D12CCA00357815 /* RNNotificationEventHandler.h */; }; + 508CE7D622D12CCA00357815 /* RNNotificationEventHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE7D422D12CCA00357815 /* RNNotificationEventHandler.m */; }; + 508CE81422D12FC700357815 /* RNNotificationCenterListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE81322D12FC600357815 /* RNNotificationCenterListener.m */; }; + 508CE81622D12FF600357815 /* RNNotificationCenterListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE81522D12FF500357815 /* RNNotificationCenterListener.h */; }; + 508CE81922D130B900357815 /* RNPushKitEventListener.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE81722D130B900357815 /* RNPushKitEventListener.h */; }; + 508CE81A22D130B900357815 /* RNPushKitEventListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE81822D130B900357815 /* RNPushKitEventListener.m */; }; + 508CE81D22D1337200357815 /* RNPushKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE81B22D1337200357815 /* RNPushKit.h */; }; + 508CE81E22D1337200357815 /* RNPushKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE81C22D1337200357815 /* RNPushKit.m */; }; + 508CE82222D1372E00357815 /* RNNotificationUtils.h in Headers */ = {isa = PBXBuildFile; fileRef = 508CE82022D1372E00357815 /* RNNotificationUtils.h */; }; + 508CE82322D1372E00357815 /* RNNotificationUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 508CE82122D1372E00357815 /* RNNotificationUtils.m */; }; + 50AD1FCA22D13ADB00E12362 /* RNPushKitEventHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 50AD1FC822D13ADB00E12362 /* RNPushKitEventHandler.h */; }; + 50AD1FCB22D13ADB00E12362 /* RNPushKitEventHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 50AD1FC922D13ADB00E12362 /* RNPushKitEventHandler.m */; }; + 50E49F0722D1E4E0007160C1 /* RNNotificationsStore.h in Headers */ = {isa = PBXBuildFile; fileRef = 50E49F0522D1E4E0007160C1 /* RNNotificationsStore.h */; }; + 50E49F0822D1E4E0007160C1 /* RNNotificationsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 50E49F0622D1E4E0007160C1 /* RNNotificationsStore.m */; }; + 50FED76622D3E06500DDD516 /* RNNotificationCenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 50FED76422D3E06500DDD516 /* RNNotificationCenter.h */; }; + 50FED76722D3E06500DDD516 /* RNNotificationCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 50FED76522D3E06500DDD516 /* RNNotificationCenter.m */; }; + 50FED76E22D3EBA800DDD516 /* RNNotificationParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 50FED76C22D3EBA800DDD516 /* RNNotificationParser.h */; }; + 50FED76F22D3EBA800DDD516 /* RNNotificationParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 50FED76D22D3EBA800DDD516 /* RNNotificationParser.m */; }; D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */ = {isa = PBXBuildFile; fileRef = D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 508CE7CE22D12B2600357815 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58B511D31A9E6C8500147676 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58B511DA1A9E6C8500147676; + remoteInfo = RNNotifications; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ 58B511D91A9E6C8500147676 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; @@ -18,6 +57,7 @@ dstPath = "include/$(PRODUCT_NAME)"; dstSubfolderSpec = 16; files = ( + 507DCCF522CE3EBD005D4E0B /* RNNotifications.h in CopyFiles */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -25,13 +65,48 @@ /* Begin PBXFileReference section */ 134814201AA4EA6300B7C361 /* libRNNotifications.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNNotifications.a; sourceTree = BUILT_PRODUCTS_DIR; }; - D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotificationsBridgeQueue.m; sourceTree = ""; }; - D85B37461CC05A1200DE9EB6 /* RNNotificationsBridgeQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationsBridgeQueue.h; sourceTree = ""; }; + 50351F8D22CD782F000713B3 /* RNEventEmitter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNEventEmitter.h; sourceTree = ""; }; + 50351F8E22CD782F000713B3 /* RNEventEmitter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNEventEmitter.m; sourceTree = ""; }; + 50351F9022CD7DF4000713B3 /* RNBridgeModule.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNBridgeModule.h; sourceTree = ""; }; + 50351F9122CD7DF4000713B3 /* RNBridgeModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNBridgeModule.m; sourceTree = ""; }; + 50351F9322CD7FF1000713B3 /* RCTConvert+RNNotifications.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RCTConvert+RNNotifications.h"; sourceTree = ""; }; + 50351F9422CD7FF1000713B3 /* RCTConvert+RNNotifications.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "RCTConvert+RNNotifications.m"; sourceTree = ""; }; + 50351F9622CD8604000713B3 /* RNCommandsHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNCommandsHandler.h; sourceTree = ""; }; + 50351F9722CD8604000713B3 /* RNCommandsHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNCommandsHandler.m; sourceTree = ""; }; + 508CE7C822D12B2600357815 /* RNNotificationsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RNNotificationsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 508CE7CA22D12B2600357815 /* RNNotificationsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationsTests.m; sourceTree = ""; }; + 508CE7CC22D12B2600357815 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 508CE7D322D12CCA00357815 /* RNNotificationEventHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationEventHandler.h; sourceTree = ""; }; + 508CE7D422D12CCA00357815 /* RNNotificationEventHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationEventHandler.m; sourceTree = ""; }; + 508CE81322D12FC600357815 /* RNNotificationCenterListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotificationCenterListener.m; sourceTree = ""; }; + 508CE81522D12FF500357815 /* RNNotificationCenterListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNotificationCenterListener.h; sourceTree = ""; }; + 508CE81722D130B900357815 /* RNPushKitEventListener.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNPushKitEventListener.h; sourceTree = ""; }; + 508CE81822D130B900357815 /* RNPushKitEventListener.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNPushKitEventListener.m; sourceTree = ""; }; + 508CE81B22D1337200357815 /* RNPushKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNPushKit.h; sourceTree = ""; }; + 508CE81C22D1337200357815 /* RNPushKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNPushKit.m; sourceTree = ""; }; + 508CE82022D1372E00357815 /* RNNotificationUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationUtils.h; sourceTree = ""; }; + 508CE82122D1372E00357815 /* RNNotificationUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationUtils.m; sourceTree = ""; }; + 50AD1FC822D13ADB00E12362 /* RNPushKitEventHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNPushKitEventHandler.h; sourceTree = ""; }; + 50AD1FC922D13ADB00E12362 /* RNPushKitEventHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNPushKitEventHandler.m; sourceTree = ""; }; + 50E49F0522D1E4E0007160C1 /* RNNotificationsStore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationsStore.h; sourceTree = ""; }; + 50E49F0622D1E4E0007160C1 /* RNNotificationsStore.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationsStore.m; sourceTree = ""; }; + 50FED76422D3E06500DDD516 /* RNNotificationCenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationCenter.h; sourceTree = ""; }; + 50FED76522D3E06500DDD516 /* RNNotificationCenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationCenter.m; sourceTree = ""; }; + 50FED76C22D3EBA800DDD516 /* RNNotificationParser.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNNotificationParser.h; sourceTree = ""; }; + 50FED76D22D3EBA800DDD516 /* RNNotificationParser.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNNotificationParser.m; sourceTree = ""; }; D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNNotifications.m; sourceTree = ""; }; D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNNotifications.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 508CE7C522D12B2600357815 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 508CE7CD22D12B2600357815 /* libRNNotifications.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 58B511D81A9E6C8500147676 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -50,24 +125,134 @@ name = Products; sourceTree = ""; }; + 508CE7C922D12B2600357815 /* RNNotificationsTests */ = { + isa = PBXGroup; + children = ( + 508CE7CA22D12B2600357815 /* RNNotificationsTests.m */, + 508CE7CC22D12B2600357815 /* Info.plist */, + ); + path = RNNotificationsTests; + sourceTree = ""; + }; + 508CE81122D12F3C00357815 /* Notifications */ = { + isa = PBXGroup; + children = ( + 50FED76422D3E06500DDD516 /* RNNotificationCenter.h */, + 50FED76522D3E06500DDD516 /* RNNotificationCenter.m */, + 50E49F0522D1E4E0007160C1 /* RNNotificationsStore.h */, + 50E49F0622D1E4E0007160C1 /* RNNotificationsStore.m */, + 508CE7D322D12CCA00357815 /* RNNotificationEventHandler.h */, + 508CE7D422D12CCA00357815 /* RNNotificationEventHandler.m */, + 508CE81522D12FF500357815 /* RNNotificationCenterListener.h */, + 508CE81322D12FC600357815 /* RNNotificationCenterListener.m */, + 50FED76C22D3EBA800DDD516 /* RNNotificationParser.h */, + 50FED76D22D3EBA800DDD516 /* RNNotificationParser.m */, + ); + name = Notifications; + sourceTree = ""; + }; + 508CE81222D12F4300357815 /* PushKitNotifications */ = { + isa = PBXGroup; + children = ( + 508CE81B22D1337200357815 /* RNPushKit.h */, + 508CE81C22D1337200357815 /* RNPushKit.m */, + 508CE81722D130B900357815 /* RNPushKitEventListener.h */, + 508CE81822D130B900357815 /* RNPushKitEventListener.m */, + 50AD1FC822D13ADB00E12362 /* RNPushKitEventHandler.h */, + 50AD1FC922D13ADB00E12362 /* RNPushKitEventHandler.m */, + ); + name = PushKitNotifications; + sourceTree = ""; + }; + 508CE81F22D1371700357815 /* Helpers */ = { + isa = PBXGroup; + children = ( + 508CE82022D1372E00357815 /* RNNotificationUtils.h */, + 508CE82122D1372E00357815 /* RNNotificationUtils.m */, + 50351F9322CD7FF1000713B3 /* RCTConvert+RNNotifications.h */, + 50351F9422CD7FF1000713B3 /* RCTConvert+RNNotifications.m */, + ); + name = Helpers; + sourceTree = ""; + }; + 50E4A03D22D220E8007160C1 /* Bridge */ = { + isa = PBXGroup; + children = ( + 50351F9022CD7DF4000713B3 /* RNBridgeModule.h */, + 50351F9122CD7DF4000713B3 /* RNBridgeModule.m */, + 50351F8D22CD782F000713B3 /* RNEventEmitter.h */, + 50351F8E22CD782F000713B3 /* RNEventEmitter.m */, + ); + name = Bridge; + sourceTree = ""; + }; 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - D85B37461CC05A1200DE9EB6 /* RNNotificationsBridgeQueue.h */, - D85B37441CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m */, + 50E4A03D22D220E8007160C1 /* Bridge */, + 508CE81222D12F4300357815 /* PushKitNotifications */, + 508CE81122D12F3C00357815 /* Notifications */, + 508CE81F22D1371700357815 /* Helpers */, D8A2F7561CB57F28002CC8F5 /* RNNotifications.h */, D8A2F7541CB57F1A002CC8F5 /* RNNotifications.m */, + 50351F9622CD8604000713B3 /* RNCommandsHandler.h */, + 50351F9722CD8604000713B3 /* RNCommandsHandler.m */, + 508CE7C922D12B2600357815 /* RNNotificationsTests */, 134814211AA4EA7D00B7C361 /* Products */, + 508CE7C822D12B2600357815 /* RNNotificationsTests.xctest */, ); sourceTree = ""; }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + 507DCCF622CE3EEF005D4E0B /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 508CE81D22D1337200357815 /* RNPushKit.h in Headers */, + 50AD1FCA22D13ADB00E12362 /* RNPushKitEventHandler.h in Headers */, + 507DCCF922CE3F04005D4E0B /* RNNotifications.h in Headers */, + 508CE7D522D12CCA00357815 /* RNNotificationEventHandler.h in Headers */, + 50FED76622D3E06500DDD516 /* RNNotificationCenter.h in Headers */, + 508CE81922D130B900357815 /* RNPushKitEventListener.h in Headers */, + 50E49F0722D1E4E0007160C1 /* RNNotificationsStore.h in Headers */, + 508CE81622D12FF600357815 /* RNNotificationCenterListener.h in Headers */, + 508CE82222D1372E00357815 /* RNNotificationUtils.h in Headers */, + 50FED76E22D3EBA800DDD516 /* RNNotificationParser.h in Headers */, + 507DCCFA22CE3F04005D4E0B /* RNEventEmitter.h in Headers */, + 507DCCFB22CE3F04005D4E0B /* RNCommandsHandler.h in Headers */, + 507DCCFC22CE3F04005D4E0B /* RCTConvert+RNNotifications.h in Headers */, + 507DCCF722CE3EF7005D4E0B /* RNBridgeModule.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ + 508CE7C722D12B2600357815 /* RNNotificationsTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 508CE7D022D12B2600357815 /* Build configuration list for PBXNativeTarget "RNNotificationsTests" */; + buildPhases = ( + 508CE7C422D12B2600357815 /* Sources */, + 508CE7C522D12B2600357815 /* Frameworks */, + 508CE7C622D12B2600357815 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 508CE7CF22D12B2600357815 /* PBXTargetDependency */, + ); + name = RNNotificationsTests; + productName = RNNotificationsTests; + productReference = 508CE7C822D12B2600357815 /* RNNotificationsTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 58B511DA1A9E6C8500147676 /* RNNotifications */ = { isa = PBXNativeTarget; buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNNotifications" */; buildPhases = ( + 507DCCF622CE3EEF005D4E0B /* Headers */, 58B511D71A9E6C8500147676 /* Sources */, 58B511D81A9E6C8500147676 /* Frameworks */, 58B511D91A9E6C8500147676 /* CopyFiles */, @@ -90,6 +275,10 @@ LastUpgradeCheck = 0610; ORGANIZATIONNAME = Facebook; TargetAttributes = { + 508CE7C722D12B2600357815 = { + CreatedOnToolsVersion = 10.1; + ProvisioningStyle = Automatic; + }; 58B511DA1A9E6C8500147676 = { CreatedOnToolsVersion = 6.1.1; }; @@ -108,23 +297,133 @@ projectRoot = ""; targets = ( 58B511DA1A9E6C8500147676 /* RNNotifications */, + 508CE7C722D12B2600357815 /* RNNotificationsTests */, ); }; /* End PBXProject section */ +/* Begin PBXResourcesBuildPhase section */ + 508CE7C622D12B2600357815 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ + 508CE7C422D12B2600357815 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 508CE7CB22D12B2600357815 /* RNNotificationsTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 58B511D71A9E6C8500147676 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 50351F9822CD8604000713B3 /* RNCommandsHandler.m in Sources */, D8A2F7551CB57F1A002CC8F5 /* RNNotifications.m in Sources */, - D85B37451CC05A0900DE9EB6 /* RNNotificationsBridgeQueue.m in Sources */, + 50351F8F22CD782F000713B3 /* RNEventEmitter.m in Sources */, + 508CE81A22D130B900357815 /* RNPushKitEventListener.m in Sources */, + 50351F9222CD7DF4000713B3 /* RNBridgeModule.m in Sources */, + 50FED76722D3E06500DDD516 /* RNNotificationCenter.m in Sources */, + 50FED76F22D3EBA800DDD516 /* RNNotificationParser.m in Sources */, + 508CE81E22D1337200357815 /* RNPushKit.m in Sources */, + 508CE7D622D12CCA00357815 /* RNNotificationEventHandler.m in Sources */, + 50E49F0822D1E4E0007160C1 /* RNNotificationsStore.m in Sources */, + 508CE82322D1372E00357815 /* RNNotificationUtils.m in Sources */, + 50351F9522CD7FF1000713B3 /* RCTConvert+RNNotifications.m in Sources */, + 50AD1FCB22D13ADB00E12362 /* RNPushKitEventHandler.m in Sources */, + 508CE81422D12FC700357815 /* RNNotificationCenterListener.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 508CE7CF22D12B2600357815 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58B511DA1A9E6C8500147676 /* RNNotifications */; + targetProxy = 508CE7CE22D12B2600357815 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ + 508CE7D122D12B2600357815 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = RNNotificationsTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 508CE7D222D12B2600357815 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = RNNotificationsTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.1; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; 58B511ED1A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -234,6 +533,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 508CE7D022D12B2600357815 /* Build configuration list for PBXNativeTarget "RNNotificationsTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 508CE7D122D12B2600357815 /* Debug */, + 508CE7D222D12B2600357815 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNNotifications" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/RNNotifications/RNNotificationsBridgeQueue.h b/RNNotifications/RNNotificationsBridgeQueue.h deleted file mode 100644 index 61ff05ba57e5baf5ea3570bfd44ea6924d892f17..0000000000000000000000000000000000000000 --- a/RNNotifications/RNNotificationsBridgeQueue.h +++ /dev/null @@ -1,19 +0,0 @@ -#import - -@interface RNNotificationsBridgeQueue : NSObject - -@property BOOL jsIsReady; -@property NSDictionary* openedRemoteNotification; -@property NSDictionary* openedLocalNotification; - -+ (nonnull instancetype)sharedInstance; - -- (void)postAction:(NSDictionary *)action withCompletionKey:(NSString *)completionKey andCompletionHandler:(void (^)())completionHandler; -- (void)postNotification:(NSDictionary *)notification; - -- (void)consumeActionsQueue:(void (^)(NSDictionary *))block; -- (void)consumeNotificationsQueue:(void (^)(NSDictionary *))block; - -- (void)completeAction:(NSString *)completionKey; - -@end \ No newline at end of file diff --git a/RNNotifications/RNNotificationsBridgeQueue.m b/RNNotifications/RNNotificationsBridgeQueue.m deleted file mode 100644 index 9555f3fb457a02bd88215dc5671e398c92ff3eb9..0000000000000000000000000000000000000000 --- a/RNNotifications/RNNotificationsBridgeQueue.m +++ /dev/null @@ -1,95 +0,0 @@ -#import "RNNotificationsBridgeQueue.h" - -@implementation RNNotificationsBridgeQueue - -NSMutableArray* actionsQueue; -NSMutableArray* notificationsQueue; -NSMutableDictionary* actionCompletionHandlers; - -+ (nonnull instancetype)sharedInstance { - static RNNotificationsBridgeQueue* sharedInstance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [self new]; - }); - - return sharedInstance; -} - -- (instancetype)init -{ - actionsQueue = [NSMutableArray new]; - notificationsQueue = [NSMutableArray new]; - actionCompletionHandlers = [NSMutableDictionary new]; - self.jsIsReady = NO; - - return self; -} - -- (void)postNotification:(NSDictionary *)notification -{ - if (!notificationsQueue) return; - [notificationsQueue insertObject:notification atIndex:0]; -} - -- (NSDictionary *)dequeueSingleNotification -{ - if (!notificationsQueue || notificationsQueue.count == 0) return nil; - - NSDictionary* notification = [notificationsQueue lastObject]; - [notificationsQueue removeLastObject]; - - return notification; -} - -- (void)consumeNotificationsQueue:(void (^)(NSDictionary *))block -{ - NSDictionary* notification; - - while ((notification = [self dequeueSingleNotification]) != nil) { - block(notification); - } - - notificationsQueue = nil; -} - -- (void)postAction:(NSDictionary *)action withCompletionKey:(NSString *)completionKey andCompletionHandler:(void (^)())completionHandler -{ - // store completion handler - actionCompletionHandlers[completionKey] = completionHandler; - - if (!actionsQueue) return; - [actionsQueue insertObject:action atIndex:0]; -} - -- (NSDictionary *)dequeueSingleAction -{ - if (!actionsQueue || actionsQueue.count == 0) return nil; - - NSDictionary* action = [actionsQueue lastObject]; - [actionsQueue removeLastObject]; - - return action; -} - -- (void)consumeActionsQueue:(void (^)(NSDictionary *))block -{ - NSDictionary* lastActionInfo; - - while ((lastActionInfo = [self dequeueSingleAction]) != nil) { - block(lastActionInfo); - } - - actionsQueue = nil; -} - -- (void)completeAction:(NSString *)completionKey -{ - void (^completionHandler)() = (void (^)())[actionCompletionHandlers valueForKey:completionKey]; - if (completionHandler) { - completionHandler(); - [actionCompletionHandlers removeObjectForKey:completionKey]; - } -} - -@end \ No newline at end of file diff --git a/RNNotifications/RNNotificationsStore.h b/RNNotifications/RNNotificationsStore.h new file mode 100644 index 0000000000000000000000000000000000000000..3e54aa1d6fc2ea9f247078764719218231fc5cde --- /dev/null +++ b/RNNotifications/RNNotificationsStore.h @@ -0,0 +1,16 @@ +#import +@import UserNotifications; + +@interface RNNotificationsStore : NSObject + +@property (nonatomic, retain) NSDictionary* initialNotification; + ++ (instancetype)sharedInstance; + +- (void)completeAction:(NSString *)completionKey; +- (void)completePresentation:(NSString *)completionKey withPresentationOptions:(UNNotificationPresentationOptions)presentationOptions; +- (void)setActionCompletionHandler:(void (^)())completionHandler withCompletionKey:(NSString *)completionKey; +- (void)setPresentationCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler withCompletionKey:(NSString *)completionKey; + + +@end diff --git a/RNNotifications/RNNotificationsStore.m b/RNNotifications/RNNotificationsStore.m new file mode 100644 index 0000000000000000000000000000000000000000..624a5d37dd461b215b45bb32248114332eb874e1 --- /dev/null +++ b/RNNotifications/RNNotificationsStore.m @@ -0,0 +1,48 @@ +#import "RNNotificationsStore.h" + +@implementation RNNotificationsStore +NSMutableDictionary* _actionCompletionHandlers; +NSMutableDictionary* _presentationCompletionHandlers; + ++ (instancetype)sharedInstance { + static RNNotificationsStore *sharedInstance = nil; + static dispatch_once_t onceToken; + + dispatch_once(&onceToken, ^{ + sharedInstance = [[RNNotificationsStore alloc] init]; + }); + return sharedInstance; +} + +- (instancetype)init { + self = [super init]; + _actionCompletionHandlers = [NSMutableDictionary new]; + _presentationCompletionHandlers = [NSMutableDictionary new]; + return self; +} + +- (void)setActionCompletionHandler:(void (^)())completionHandler withCompletionKey:(NSString *)completionKey { + _actionCompletionHandlers[completionKey] = completionHandler; +} + +- (void)setPresentationCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler withCompletionKey:(NSString *)completionKey { + _presentationCompletionHandlers[completionKey] = completionHandler; +} + +- (void)completeAction:(NSString *)completionKey { + void (^completionHandler)() = (void (^)())[_actionCompletionHandlers valueForKey:completionKey]; + if (completionHandler) { + completionHandler(); + [_actionCompletionHandlers removeObjectForKey:completionKey]; + } +} + +- (void)completePresentation:(NSString *)completionKey withPresentationOptions:(UNNotificationPresentationOptions)presentationOptions { + void (^completionHandler)() = (void (^)(UNNotificationPresentationOptions))[_presentationCompletionHandlers valueForKey:completionKey]; + if (completionHandler) { + completionHandler(presentationOptions); + [_actionCompletionHandlers removeObjectForKey:completionKey]; + } +} + +@end diff --git a/RNNotifications/RNNotificationsTests/Info.plist b/RNNotifications/RNNotificationsTests/Info.plist new file mode 100644 index 0000000000000000000000000000000000000000..6c40a6cd0c4af2f0d93b697fbfb066793681b045 --- /dev/null +++ b/RNNotifications/RNNotificationsTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/RNNotifications/RNNotificationsTests/RNNotificationsTests.m b/RNNotifications/RNNotificationsTests/RNNotificationsTests.m new file mode 100644 index 0000000000000000000000000000000000000000..bfb9fa6b3478a21c18559a1421dd71e0e7ab8248 --- /dev/null +++ b/RNNotifications/RNNotificationsTests/RNNotificationsTests.m @@ -0,0 +1,37 @@ +// +// RNNotificationsTests.m +// RNNotificationsTests +// +// Created by Yogev Ben David on 06/07/2019. +// Copyright © 2019 Facebook. All rights reserved. +// + +#import + +@interface RNNotificationsTests : XCTestCase + +@end + +@implementation RNNotificationsTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/RNNotifications/RNPushKit.h b/RNNotifications/RNPushKit.h new file mode 100644 index 0000000000000000000000000000000000000000..3a0b22d419a5a6dff75cb396135d975f1118baff --- /dev/null +++ b/RNNotifications/RNPushKit.h @@ -0,0 +1,9 @@ +#import +#import "RNPushKitEventListener.h" +@import PushKit; + +@interface RNPushKit : NSObject + +- (instancetype)initWithEventHandler:(RNPushKitEventHandler *)pushKitEventHandler; + +@end diff --git a/RNNotifications/RNPushKit.m b/RNNotifications/RNPushKit.m new file mode 100644 index 0000000000000000000000000000000000000000..a0915a2ee6470379dd73c6cb06c06e96e8a5210e --- /dev/null +++ b/RNNotifications/RNPushKit.m @@ -0,0 +1,19 @@ +#import "RNPushKit.h" + +@implementation RNPushKit { + RNPushKitEventListener* _pushKitEventListener; +} + +- (instancetype)initWithEventHandler:(RNPushKitEventHandler *)pushKitEventHandler { + self = [super init]; + + _pushKitEventListener = [[RNPushKitEventListener alloc] initWithPushKitEventHandler:pushKitEventHandler]; + + PKPushRegistry* pushKitRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()]; + pushKitRegistry.delegate = _pushKitEventListener; + pushKitRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; + + return self; +} + +@end diff --git a/RNNotifications/RNPushKitEventHandler.h b/RNNotifications/RNPushKitEventHandler.h new file mode 100644 index 0000000000000000000000000000000000000000..e627cd2c52767d17d48b4b803ad86c65f63d1df5 --- /dev/null +++ b/RNNotifications/RNPushKitEventHandler.h @@ -0,0 +1,10 @@ +#import +#import "RNNotificationEventHandler.h" + +@interface RNPushKitEventHandler : RNNotificationEventHandler + +- (void)registeredWithToken:(NSString *)token; + +- (void)didReceiveIncomingPushWithPayload:(NSDictionary *)payload; + +@end diff --git a/RNNotifications/RNPushKitEventHandler.m b/RNNotifications/RNPushKitEventHandler.m new file mode 100644 index 0000000000000000000000000000000000000000..2e2c99ed415ff2db06ee98ed965e7c9f0fbaf774 --- /dev/null +++ b/RNNotifications/RNPushKitEventHandler.m @@ -0,0 +1,14 @@ +#import "RNPushKitEventHandler.h" +#import "RNEventEmitter.h" + +@implementation RNPushKitEventHandler + +- (void)registeredWithToken:(NSString *)token { + [RNEventEmitter sendEvent:RNPushKitRegistered body:@{@"pushKitToken": token}]; +} + +- (void)didReceiveIncomingPushWithPayload:(NSDictionary *)payload { + [RNEventEmitter sendEvent:RNPushKitNotificationReceived body:payload]; +} + +@end diff --git a/RNNotifications/RNPushKitEventListener.h b/RNNotifications/RNPushKitEventListener.h new file mode 100644 index 0000000000000000000000000000000000000000..7cb5be8f3a01aebbabf411b037db798bd5f3fda1 --- /dev/null +++ b/RNNotifications/RNPushKitEventListener.h @@ -0,0 +1,9 @@ +#import +@import PushKit; +#import "RNPushKitEventHandler.h" + +@interface RNPushKitEventListener : NSObject + +- (instancetype)initWithPushKitEventHandler:(RNPushKitEventHandler *)pushKitEventHandler; + +@end diff --git a/RNNotifications/RNPushKitEventListener.m b/RNNotifications/RNPushKitEventListener.m new file mode 100644 index 0000000000000000000000000000000000000000..88d7a888c43f1b631b57ed5efd35350a770bcf56 --- /dev/null +++ b/RNNotifications/RNPushKitEventListener.m @@ -0,0 +1,26 @@ +#import "RNPushKitEventListener.h" +#import "RNNotificationUtils.h" + +@implementation RNPushKitEventListener { + PKPushRegistry* _pushRegistry; + RNPushKitEventHandler* _pushKitEventHandler; +} + +- (instancetype)initWithPushKitEventHandler:(RNPushKitEventHandler *)pushKitEventHandler { + self = [super init]; + + _pushRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()]; + _pushKitEventHandler = pushKitEventHandler; + + return self; +} + +- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type { + [_pushKitEventHandler registeredWithToken:[RNNotificationUtils deviceTokenToString:credentials.token]]; +} + +- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type { + [_pushKitEventHandler didReceiveIncomingPushWithPayload:payload.dictionaryPayload]; +} + +@end diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000000000000000000000000000000000000..f25946816ed74ef62f5963a02af894c66d23227b --- /dev/null +++ b/babel.config.js @@ -0,0 +1,12 @@ +module.exports = function (api) { + api && api.cache(false); + return { + presets: [ + 'module:metro-react-native-babel-preset' + ], + plugins: [ + '@babel/plugin-proposal-export-namespace-from', + '@babel/plugin-proposal-export-default-from' + ] + }; +}; diff --git a/docs/advancedIos.md b/docs/advancedIos.md index 9e44bde671158c2b25eea0a66aa3a593d7d2cdca..27f69ce15383f6279500a871f1854a0be6d2e547 100644 --- a/docs/advancedIos.md +++ b/docs/advancedIos.md @@ -91,33 +91,31 @@ After [preparing your app to receive VoIP push notifications](https://developer. #import ``` -And the following methods: +### Listen to PushKit notifications +On receiving PushKit notification, a `pushKitNotificationReceived` event will be fired with the notification payload. ```objective-c -// PushKit API Support -- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type -{ - [RNNotifications didUpdatePushCredentials:credentials forType:type]; -} - -- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type -{ - [RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload]; -} -``` +#import "RNNotifications.h" +#import +``` In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`: ```javascript constructor() { - NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); - NotificationsIOS.registerPushKit(); + NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); + NotificationsIOS.addEventListener('pushKitNotificationReceived', this.onPushKitNotificationReceived.bind(this)); + NotificationsIOS.registerPushKit(); } onPushKitRegistered(deviceToken) { console.log("PushKit Token Received: " + deviceToken); } +onPushKitNotificationReceived(notification) { + console.log('PushKit notification Received: ' + JSON.stringify(notification)); +} + componentWillUnmount() { // Don't forget to remove the event listeners to prevent memory leaks! NotificationsIOS.removeEventListener('pushKitRegistered', onPushKitRegistered(this)); @@ -150,22 +148,7 @@ Notification **actions** allow the user to interact with a given notification. Notification **categories** allow you to group multiple actions together, and to connect the actions with the push notification itself. -In order to support interactive notifications, firstly add the following methods to `appDelegate.m` file: - -```objective-c -// Required for the notification actions. -- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [RNNotifications handleActionWithIdentifier:identifier forLocalNotification:notification withResponseInfo:responseInfo completionHandler:completionHandler]; -} - -- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler -{ - [RNNotifications handleActionWithIdentifier:identifier forRemoteNotification:userInfo withResponseInfo:responseInfo completionHandler:completionHandler]; -} -``` - -Then, follow the basic workflow of adding interactive notifications to your app: +Follow the basic workflow of adding interactive notifications to your app: 1. Config the actions. 2. Group actions together into categories. @@ -182,26 +165,18 @@ import NotificationsIOS, { NotificationAction, NotificationCategory } from 'reac let upvoteAction = new NotificationAction({ activationMode: "background", title: String.fromCodePoint(0x1F44D), - identifier: "UPVOTE_ACTION" -}, (action, completed) => { - console.log("ACTION RECEIVED"); - console.log(JSON.stringify(action)); - - // You must call to completed(), otherwise the action will not be triggered - completed(); + identifier: "UPVOTE_ACTION", + textInput: { + buttonTitle: 'title', + placeholder: 'placeholder text' + } }); let replyAction = new NotificationAction({ activationMode: "background", title: "Reply", - behavior: "textInput", authenticationRequired: true, identifier: "REPLY_ACTION" -}, (action, completed) => { - console.log("ACTION RECEIVED"); - console.log(action); - - completed(); }); ``` @@ -212,8 +187,7 @@ We will group `upvote` action and `reply` action into a single category: `EXAMPL ```javascript let exampleCategory = new NotificationCategory({ identifier: "EXAMPLE_CATEGORY", - actions: [upvoteAction, replyAction], - context: "default" + actions: [upvoteAction, replyAction] }); ``` @@ -230,8 +204,8 @@ Notification payload should look like this: ```javascript { aps: { - // ... (alert, sound, badge, etc) - category: "EXAMPLE_CATEGORY" + // ... (alert, sound, badge, etc) + category: "EXAMPLE_CATEGORY" } } ``` @@ -245,9 +219,7 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/ - `activationMode` - Indicating whether the app should activate to the foreground or background. - `foreground` (default) - Activate the app and put it in the foreground. - `background` - Activate the app and put it in the background. If the app is already in the foreground, it remains in the foreground. -- `behavior` - Indicating additional behavior that the action supports. - - `default` - No additional behavior. - - `textInput` - When button is tapped, the action opens a text input. the text will be delivered to your action callback. +- `textInput` - `TextInput` payload, when supplied, the system will present text input in this action. - `destructive` - A Boolean value indicating whether the action is destructive. When the value of this property is `true`, the system displays the corresponding button differently to indicate that the action is destructive. - `authenticationRequired` - A Boolean value indicating whether the user must unlock the device before the action is performed. @@ -255,9 +227,11 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/ - `identifier` - The name of the action group (must be unique). - `actions` - An array of `NotificationAction` objects, which related to this category. -- `context` - Indicating the amount of space available for displaying actions in a notification. - - `default` (default) - Displayes up to 4 actions (full UI). - - `minimal` - Displays up tp 2 actions (minimal UI). + +### `TextInput` Payload + +- `buttonTitle` - Title of the `send` button. +- `placeholder` - Placeholder for the `textInput`. #### Get and set application icon badges count (iOS only) diff --git a/docs/installation.md b/docs/installation.md index 12e33d02f92935ae76e33dda9d687980e06d4de5..f685007e6d611414bce7e74d3798117c5bec3b76 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -20,17 +20,24 @@ Then, to enable notifications support add the following line at the top of your #import "RNNotifications.h" ``` -And the following methods to support registration and receiving notifications: +Start monitor notifications in: `application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions` ```objective-c -// Required to register for notifications -- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings -{ - [RNNotifications didRegisterUserNotificationSettings:notificationSettings]; + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [RNNotifications startMonitorNotifications]; // -> Add this line + + return YES; } -- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken -{ +``` + + +And add the following methods to support registration: + +```objective-c + +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; } @@ -38,16 +45,6 @@ And the following methods to support registration and receiving notifications: [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; } -// Required for the notification event. -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification { - [RNNotifications didReceiveRemoteNotification:notification]; -} - -// Required for the localNotification event. -- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification -{ - [RNNotifications didReceiveLocalNotification:notification]; -} ``` ## Android diff --git a/docs/localNotifications.md b/docs/localNotifications.md index 9747146c6d99bec2f91fa6573abe2075ffb5b486..8f1d2b94838dec8baa1359ca8c0d56785289fb8d 100644 --- a/docs/localNotifications.md +++ b/docs/localNotifications.md @@ -10,9 +10,9 @@ Example: ```javascript let localNotification = NotificationsIOS.localNotification({ - alertBody: "Local notificiation!", - alertTitle: "Local Notification Title", - soundName: "chime.aiff", + body: "Local notificiation!", + title: "Local Notification Title", + sound: "chime.aiff", silent: false, category: "SOME_CATEGORY", userInfo: { } @@ -22,10 +22,10 @@ let localNotification = NotificationsIOS.localNotification({ 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. +- `body`- The message displayed in the notification alert. +- `title`- The title of the notification, displayed in the notifications center. - `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 -- will play default sound if unspecified). This must be the filename of a sound included in the application bundle; the sound must be 30 seconds or less and should be encoded with linear PCM or IMA4. +- `sound`- The sound played when the notification is fired (optional -- will play default sound if unspecified). This must be the filename of a sound included in the application bundle; the sound must be 30 seconds or less and should be encoded with linear PCM or IMA4. - `silent`- Whether the notification sound should be suppressed (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. @@ -38,9 +38,9 @@ Example: ```javascript let someLocalNotification = NotificationsIOS.localNotification({ - alertBody: "Local notificiation!", - alertTitle: "Local Notification Title", - soundName: "chime.aiff", + body: "Local notificiation!", + title: "Local Notification Title", + sound: "chime.aiff", category: "SOME_CATEGORY", userInfo: { } }); diff --git a/docs/notificationsEvents.md b/docs/notificationsEvents.md index 1a1c0b975ba15bcfa0714c2d9b05c66e77b30a3b..21cdef361f174916dbfcc6851b4772fd37bdacc7 100644 --- a/docs/notificationsEvents.md +++ b/docs/notificationsEvents.md @@ -5,40 +5,35 @@ When a push notification is received by the device, the application can be in one of the following states: -1. **Forground:** When the app is running and is used by the user right now; in this case, a `notificationReceivedForeground` event will be fired. -2. **Background:** When the app is running in a background state; in this case, a `notificationReceivedBackground` event will be fired. +1. **Forground:** When the app is running and is used by the user right now; in this case, a `notificationReceivedForeground` event will be fired, do not forget to invoke `completion()` callback. -Finally, when a notification is _opened_ by the device user (i.e. tapped-on), a `notificationOpened` event is fired. +Finally, when a notification is _opened_ by the device user (i.e. tapped-on), a `notificationOpened` event is fired, here as well you need to remember invoking `completion()` callback. Example: ```javascript constructor() { this._boundOnNotificationReceivedForeground = this.onNotificationReceivedForeground.bind(this); - this._boundOnNotificationReceivedBackground = this.onNotificationReceivedBackground.bind(this); this._boundOnNotificationOpened = this.onNotificationOpened.bind(this); NotificationsIOS.addEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground); - NotificationsIOS.addEventListener('notificationReceivedBackground', this._boundOnNotificationReceivedBackground); NotificationsIOS.addEventListener('notificationOpened', this._boundOnNotificationOpened); } -onNotificationReceivedForeground(notification) { +onNotificationReceivedForeground(notification, completion) { + completion({alert: true, sound: false, badge: false}); console.log("Notification Received - Foreground", notification); } -onNotificationReceivedBackground(notification) { - console.log("Notification Received - Background", notification); -} - -onNotificationOpened(notification) { +onNotificationOpened(notification, completion, action) { console.log("Notification opened by device user", notification); + console.log(`Notification opened with an action identifier: ${action.identifier} and response text: ${action.text}`, notification); + completion(); } componentWillUnmount() { // Don't forget to remove the event listeners to prevent memory leaks! NotificationsIOS.removeEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground); - NotificationsIOS.removeEventListener('notificationReceivedBackground', this._boundOnNotificationReceivedBackground); NotificationsIOS.removeEventListener('notificationOpened', this._boundOnNotificationOpened); } ``` @@ -54,12 +49,6 @@ When you receive a push notification, you'll get an instance of `IOSNotification - **`getData()`**- returns the data payload (additional info) of the notification. - **`getType()`**- returns `managed` for managed notifications, otherwise returns `regular`. -### Background Queue (Important - please read!) - -When a push notification is opened but the app is not running, the application will be in a **cold launch** state, until the JS engine is up and ready to handle the notification. -The application will collect the events (notifications, actions, etc.) that happend during the cold launch for you. - -When your app is ready (most of the time it's after the call to `requestPermissions()`), just call to `NotificationsIOS.consumeBackgroundQueue();` in order to consume the background queue. For more info see `index.ios.js` in the example app. ## Android diff --git a/e2e/Notifications.test.js b/e2e/Notifications.test.js new file mode 100644 index 0000000000000000000000000000000000000000..fd39b4e20ceb7d3b332ac9b9a3713c44e4f9f048 --- /dev/null +++ b/e2e/Notifications.test.js @@ -0,0 +1,56 @@ +const Utils = require('./Utils'); +const { elementByLabel } = Utils; + +describe('Notifications', () => { + describe('Foreground', () => { + beforeEach(async () => { + await device.relaunchApp({permissions: {notifications: 'YES'}}); + }); + + it('Receive notification', async () => { + await device.sendUserNotification(createNotification({link: 'foreground/notification'})); + await expect(elementByLabel('foreground/notification')).toBeVisible(); + }); + + it('Click notification', async () => { + await device.sendUserNotification(createNotification({link: 'foreground/notification/click', showAlert: true})); + await expect(elementByLabel('Notification Clicked: foreground/notification/click')).toBeVisible(); + }); + }); + + describe('Background', () => { + beforeEach(async () => { + await device.launchApp({newInstance: true, permissions: {notifications: 'YES'}}); + }); + + it('Receive notification', async () => { + device.sendToHome(); + await expect(elementByLabel('background/notification')).toBeNotVisible(); + device.launchApp({newInstance: false, userNotification: createNotification({link: 'background/notification'})}); + await expect(elementByLabel('background/notification')).toBeVisible(); + }); + }); + + describe('Dead state', () => { + it('Receive notification', async () => { + await device.launchApp({newInstance: true, userNotification: createNotification({link: 'deadState/notification'})}); + await expect(elementByLabel('deadState/notification')).toBeVisible(); + }); + }); +}); + +function createNotification({link, showAlert}) { + return { + trigger: { + type: 'push' + }, + title: 'From push', + subtitle: 'Subtitle', + body: 'Body', + badge: 1, + payload: { + link, + showAlert + } + }; +} diff --git a/e2e/Utils.js b/e2e/Utils.js new file mode 100644 index 0000000000000000000000000000000000000000..938745504f7ee07180e13f6bb92e1d84b8021d93 --- /dev/null +++ b/e2e/Utils.js @@ -0,0 +1,18 @@ +const exec = require('shell-utils').exec; + +module.exports = { + elementByLabel: (label) => { + return element(by.text(label)); + }, + elementById: (id) => { + return element(by.id(id)); + }, + tapBackIos: () => { + try { + return element(by.traits(['button']).and(by.label('Back'))).atIndex(0).tap(); + } catch (err) { + return element(by.type('_UIModernBarButton').and(by.label('Back'))).tap(); + } + }, + sleep: ms => new Promise(res => setTimeout(res, ms)) +}; diff --git a/e2e/config.json b/e2e/config.json new file mode 100644 index 0000000000000000000000000000000000000000..4dac170d3611e1aedf2d44b49c675b6ded1b78c1 --- /dev/null +++ b/e2e/config.json @@ -0,0 +1,6 @@ +{ + "setupTestFrameworkScriptFile" : "./init.js", + "testEnvironment": "node", + "bail": true, + "verbose": true +} diff --git a/e2e/init.js b/e2e/init.js new file mode 100644 index 0000000000000000000000000000000000000000..a301c90e6cfc5722bfd9ec167737e2aad2e619ae --- /dev/null +++ b/e2e/init.js @@ -0,0 +1,30 @@ +const detox = require('detox'); +const config = require('../package.json').detox; +const exec = require('shell-utils').exec; +const adapter = require('detox/runners/jest/adapter'); + +jest.setTimeout(300000); +jasmine.getEnv().addReporter(adapter); + +beforeAll(async () => { + await detox.init(config, {launchApp: false}); + disableAndroidEmulatorAnimations(); +}); + +afterAll(async () => { + await adapter.afterAll(); + await detox.cleanup(); +}); + +beforeEach(async () => { + await adapter.beforeEach(); +}); + +function disableAndroidEmulatorAnimations() { + if (device.getPlatform() === 'android') { + const deviceId = device._deviceId; + exec.execAsync(`adb -s ${deviceId} shell settings put global window_animation_scale 0.0`); + exec.execAsync(`adb -s ${deviceId} shell settings put global transition_animation_scale 0.0`); + exec.execAsync(`adb -s ${deviceId} shell settings put global animator_duration_scale 0.0`); + } +} diff --git a/example/index.ios.js b/example/index.ios.js index 5a2d5c3cf348398777109a160d19ecc4e0de0f24..a764bfab74367a4c16720182dab99706c96f89d3 100644 --- a/example/index.ios.js +++ b/example/index.ios.js @@ -1,137 +1,140 @@ -/** - * Sample React Native App - * https://github.com/facebook/react-native - */ - import { AppRegistry, StyleSheet, + View, Text, - View + Button } from 'react-native'; import React, {Component} from 'react'; import NotificationsIOS, { NotificationAction, NotificationCategory } from 'react-native-notifications'; let upvoteAction = new NotificationAction({ - activationMode: "background", + activationMode: 'background', title: String.fromCodePoint(0x1F44D), - identifier: "UPVOTE_ACTION" -}, (action, completed) => { - NotificationsIOS.log("ACTION RECEIVED"); - NotificationsIOS.log(JSON.stringify(action)); - - completed(); + identifier: 'UPVOTE_ACTION' }); let replyAction = new NotificationAction({ - activationMode: "background", - title: "Reply", - behavior: "textInput", + activationMode: 'background', + title: 'Reply', authenticationRequired: true, - identifier: "REPLY_ACTION" -}, (action, completed) => { - console.log("ACTION RECEIVED"); - console.log(action); - - completed(); -}); - -let cat = new NotificationCategory({ - identifier: "SOME_CATEGORY", - actions: [upvoteAction, replyAction], - context: "default" + textInput: { + buttonTitle: 'Reply now', + placeholder: 'Insert message' + }, + identifier: 'REPLY_ACTION' }); class NotificationsExampleApp extends Component { constructor() { super(); - NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); - NotificationsIOS.requestPermissions([cat]); + this.state = { + notifications: [] + }; - NotificationsIOS.consumeBackgroundQueue(); + NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); + NotificationsIOS.addEventListener('remoteNotificationsRegistrationFailed', this.onPushRegisteredFailed.bind(this)); NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); NotificationsIOS.registerPushKit(); NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); - NotificationsIOS.addEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this)); NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this)); + NotificationsIOS.addEventListener('pushKitNotificationReceived', this.onPushKitNotificationReceived.bind(this)); + } + + async componentDidMount() { + const initialNotification = await NotificationsIOS.getInitialNotification(); + if (initialNotification) { + this.setState({notifications: [initialNotification.getData().link, ...this.state.notifications]}); + } + } onPushRegistered(deviceToken) { - console.log("Device Token Received: " + deviceToken); + console.log('Device Token Received: ' + deviceToken); } - onPushKitRegistered(deviceToken) { - console.log("PushKit Token Received: " + deviceToken); + onPushRegisteredFailed(error) { + console.log('Remote notifiction registration failed: ' + error); } - onNotificationReceivedForeground(notification) { - console.log("Notification Received Foreground: " + JSON.stringify(notification)); + onPushKitRegistered(deviceToken) { + console.log('PushKit Token Received: ' + deviceToken); } - onNotificationReceivedBackground(notification) { - NotificationsIOS.log("Notification Received Background: " + JSON.stringify(notification)); + onPushKitNotificationReceived(notification) { + console.log('PushKit notification Received: ' + JSON.stringify(notification)); + } - let localNotification = NotificationsIOS.localNotification({ - alertBody: "Received background notificiation!", - alertTitle: "Local Notification Title", - alertAction: "Click here to open", - soundName: "chime.aiff", - category: "SOME_CATEGORY", - userInfo: notification.getData() + onNotificationReceivedForeground(notification, completion) { + console.log('Notification Received Foreground with title: ' + JSON.stringify(notification)); + this.setState({ + notifications: [...this.state.notifications, notification.getData().link] }); - // if you want to fire the local notification 10 seconds later, - // add the following line to the notification payload: - // fireDate: new Date(Date.now() + (10 * 1000)).toISOString() - - // NotificationsIOS.backgroundTimeRemaining(time => NotificationsIOS.log("remaining background time: " + time)); + completion({alert: notification.getData().showAlert, sound: false, badge: false}); + } - // NotificationsIOS.cancelLocalNotification(localNotification); + onNotificationOpened(notification, completion, action) { + console.log('Notification Opened: ' + JSON.stringify(notification) + JSON.stringify(action)); + this.setState({ + notifications: [...this.state.notifications, `Notification Clicked: ${notification.getData().link}`] + }); + completion(); } - onNotificationOpened(notification) { - console.log("Notification Opened: " + JSON.stringify(notification)); + renderNotification(notification) { + return {`${notification}`}; } render() { + const notifications = this.state.notifications.map((notification, idx) => + ( + + {this.renderNotification(notification)} + + )); + return ( - - Welcome to React Native Notifications Demo App! - - - To get started, edit index.ios.js - - - Press Cmd+R to reload,{'\n'} - Cmd+D or shake for dev menu - +