Commit 1c4b350b authored by Yogev Ben David's avatar Yogev Ben David Committed by GitHub

Merge pull request #342 from wix/newAPIChangesIOS

Migrate to new iOS api's, remove unnecessary api functions
parents 882775fb 9cd8f4ac
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"parser": "babel-eslint", "parser": "babel-eslint",
"env": { "env": {
"node": true, "node": true,
"mocha": true, "jest": true,
"es6": true "es6": true
}, },
"extends": "eslint:recommended", "extends": "eslint:recommended",
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
"no-trailing-spaces": "error", "no-trailing-spaces": "error",
"quotes": [ "quotes": [
"error", "error",
"double" "single"
], ],
"semi": [ "semi": [
"error", "error",
......
...@@ -178,3 +178,7 @@ jspm_packages ...@@ -178,3 +178,7 @@ jspm_packages
# Optional REPL history # Optional REPL history
.node_repl_history .node_repl_history
coverage/
package-lock.json
\ No newline at end of file
language: node_js language: node_js
node_js: node_js:
- "7" - "10"
#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
#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<NSString *, id> *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<NSString *, id> *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<NSString *, id> *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
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface RNBridgeModule : NSObject <RCTBridgeModule>
@end
#import "RNBridgeModule.h"
#import "RNCommandsHandler.h"
#import "RCTConvert+RNNotifications.h"
#import "RNNotificationsStore.h"
#import <React/RCTBridgeDelegate.h>
@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<NSString *> *)identifiers) {
[_commandsHandler removeDeliveredNotifications:identifiers];
}
RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback) {
[_commandsHandler getDeliveredNotifications:callback];
}
#endif
@end
#import <Foundation/Foundation.h>
#import <React/RCTBridge.h>
#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<NSString *> *)identifiers;
- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback;
@end
#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<NSString *> *)identifiers {
[_notificationCenter removeDeliveredNotifications:identifiers];
}
- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback {
[_notificationCenter getDeliveredNotifications:callback];
}
@end
#import <React/RCTEventEmitter.h>
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 <RCTBridgeModule>
+ (void)sendEvent:(NSString *)event body:(NSDictionary *)body;
@end
#import "RNEventEmitter.h"
@implementation RNEventEmitter
RCT_EXPORT_MODULE();
-(NSArray<NSString *> *)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
#import <Foundation/Foundation.h>
#import <React/RCTBridge.h>
@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<NSString *> *)identifiers;
- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback;
- (void)cancelAllLocalNotifications;
@end
#import "RNNotificationCenter.h"
#import "RCTConvert+RNNotifications.h"
@implementation RNNotificationCenter
- (void)requestPermissionsWithCategories:(NSArray *)json {
NSMutableSet<UNNotificationCategory *>* 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<NSString *> *)identifiers {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center removeDeliveredNotificationsWithIdentifiers:identifiers];
}
- (void)getDeliveredNotifications:(RCTResponseSenderBlock)callback {
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
[center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
NSMutableArray<NSDictionary *> *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
#import <Foundation/Foundation.h>
@import UserNotifications;
#import "RNNotificationEventHandler.h"
@interface RNNotificationCenterListener : NSObject <UNUserNotificationCenterDelegate>
- (instancetype)initWithNotificationEventHandler:(RNNotificationEventHandler *)notificationEventHandler;
@end
#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
#import <Foundation/Foundation.h>
@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
#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
#import <Foundation/Foundation.h>
@import UserNotifications;
@interface RNNotificationParser : NSObject
+ (NSDictionary *)parseNotificationResponse:(UNNotificationResponse *)response;
+ (NSDictionary *)parseNotification:(UNNotification *)notification;
@end
#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
#import <Foundation/Foundation.h>
@interface RNNotificationUtils : NSObject
+ (NSString *)deviceTokenToString:(NSData *)deviceToken;
@end
#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
@import UIKit; @import UIKit;
#import <React/RCTBridgeModule.h>
#import <PushKit/PushKit.h> #import <PushKit/PushKit.h>
@import UserNotifications;
@interface RNNotifications : NSObject <RCTBridgeModule> @interface RNNotifications : NSObject
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken; + (instancetype)sharedInstance;
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type;
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification; + (void)startMonitorNotifications;
+ (void)didReceiveLocalNotification:(UILocalNotification *)notification; + (void)startMonitorPushKitNotifications;
+ (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler; + (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken;
+ (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler; + (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
@end @end
This diff is collapsed.
#import <Foundation/Foundation.h>
@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
#import "RNNotificationsBridgeQueue.h"
@implementation RNNotificationsBridgeQueue
NSMutableArray<NSDictionary *>* actionsQueue;
NSMutableArray<NSDictionary *>* 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
#import <Foundation/Foundation.h>
@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
#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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
//
// RNNotificationsTests.m
// RNNotificationsTests
//
// Created by Yogev Ben David on 06/07/2019.
// Copyright © 2019 Facebook. All rights reserved.
//
#import <XCTest/XCTest.h>
@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
#import <UIKit/UIKit.h>
#import "RNPushKitEventListener.h"
@import PushKit;
@interface RNPushKit : NSObject
- (instancetype)initWithEventHandler:(RNPushKitEventHandler *)pushKitEventHandler;
@end
#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
#import <Foundation/Foundation.h>
#import "RNNotificationEventHandler.h"
@interface RNPushKitEventHandler : RNNotificationEventHandler
- (void)registeredWithToken:(NSString *)token;
- (void)didReceiveIncomingPushWithPayload:(NSDictionary *)payload;
@end
#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
#import <Foundation/Foundation.h>
@import PushKit;
#import "RNPushKitEventHandler.h"
@interface RNPushKitEventListener : NSObject <PKPushRegistryDelegate>
- (instancetype)initWithPushKitEventHandler:(RNPushKitEventHandler *)pushKitEventHandler;
@end
#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
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'
]
};
};
...@@ -91,33 +91,31 @@ After [preparing your app to receive VoIP push notifications](https://developer. ...@@ -91,33 +91,31 @@ After [preparing your app to receive VoIP push notifications](https://developer.
#import <PushKit/PushKit.h> #import <PushKit/PushKit.h>
``` ```
And the following methods: ### Listen to PushKit notifications
On receiving PushKit notification, a `pushKitNotificationReceived` event will be fired with the notification payload.
```objective-c ```objective-c
// PushKit API Support #import "RNNotifications.h"
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type #import <PushKit/PushKit.h>
{ ```
[RNNotifications didUpdatePushCredentials:credentials forType:type];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
[RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload];
}
```
In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`: In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`:
```javascript ```javascript
constructor() { constructor() {
NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this));
NotificationsIOS.registerPushKit(); NotificationsIOS.addEventListener('pushKitNotificationReceived', this.onPushKitNotificationReceived.bind(this));
NotificationsIOS.registerPushKit();
} }
onPushKitRegistered(deviceToken) { onPushKitRegistered(deviceToken) {
console.log("PushKit Token Received: " + deviceToken); console.log("PushKit Token Received: " + deviceToken);
} }
onPushKitNotificationReceived(notification) {
console.log('PushKit notification Received: ' + JSON.stringify(notification));
}
componentWillUnmount() { componentWillUnmount() {
// Don't forget to remove the event listeners to prevent memory leaks! // Don't forget to remove the event listeners to prevent memory leaks!
NotificationsIOS.removeEventListener('pushKitRegistered', onPushKitRegistered(this)); NotificationsIOS.removeEventListener('pushKitRegistered', onPushKitRegistered(this));
...@@ -150,22 +148,7 @@ Notification **actions** allow the user to interact with a given notification. ...@@ -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. 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: Follow the basic workflow of adding interactive notifications to your app:
```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:
1. Config the actions. 1. Config the actions.
2. Group actions together into categories. 2. Group actions together into categories.
...@@ -182,26 +165,18 @@ import NotificationsIOS, { NotificationAction, NotificationCategory } from 'reac ...@@ -182,26 +165,18 @@ import NotificationsIOS, { NotificationAction, NotificationCategory } from 'reac
let upvoteAction = new NotificationAction({ let upvoteAction = new NotificationAction({
activationMode: "background", activationMode: "background",
title: String.fromCodePoint(0x1F44D), title: String.fromCodePoint(0x1F44D),
identifier: "UPVOTE_ACTION" identifier: "UPVOTE_ACTION",
}, (action, completed) => { textInput: {
console.log("ACTION RECEIVED"); buttonTitle: 'title',
console.log(JSON.stringify(action)); placeholder: 'placeholder text'
}
// You must call to completed(), otherwise the action will not be triggered
completed();
}); });
let replyAction = new NotificationAction({ let replyAction = new NotificationAction({
activationMode: "background", activationMode: "background",
title: "Reply", title: "Reply",
behavior: "textInput",
authenticationRequired: true, authenticationRequired: true,
identifier: "REPLY_ACTION" 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 ...@@ -212,8 +187,7 @@ We will group `upvote` action and `reply` action into a single category: `EXAMPL
```javascript ```javascript
let exampleCategory = new NotificationCategory({ let exampleCategory = new NotificationCategory({
identifier: "EXAMPLE_CATEGORY", identifier: "EXAMPLE_CATEGORY",
actions: [upvoteAction, replyAction], actions: [upvoteAction, replyAction]
context: "default"
}); });
``` ```
...@@ -230,8 +204,8 @@ Notification payload should look like this: ...@@ -230,8 +204,8 @@ Notification payload should look like this:
```javascript ```javascript
{ {
aps: { aps: {
// ... (alert, sound, badge, etc) // ... (alert, sound, badge, etc)
category: "EXAMPLE_CATEGORY" category: "EXAMPLE_CATEGORY"
} }
} }
``` ```
...@@ -245,9 +219,7 @@ The [example app](https://github.com/wix/react-native-notifications/tree/master/ ...@@ -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. - `activationMode` - Indicating whether the app should activate to the foreground or background.
- `foreground` (default) - Activate the app and put it in the foreground. - `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. - `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. - `textInput` - `TextInput` payload, when supplied, the system will present text input in this action.
- `default` - No additional behavior.
- `textInput` - When button is tapped, the action opens a text input. the text will be delivered to your action callback.
- `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. - `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. - `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/ ...@@ -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). - `identifier` - The name of the action group (must be unique).
- `actions` - An array of `NotificationAction` objects, which related to this category. - `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). ### `TextInput` Payload
- `minimal` - Displays up tp 2 actions (minimal UI).
- `buttonTitle` - Title of the `send` button.
- `placeholder` - Placeholder for the `textInput`.
#### Get and set application icon badges count (iOS only) #### Get and set application icon badges count (iOS only)
......
...@@ -20,17 +20,24 @@ Then, to enable notifications support add the following line at the top of your ...@@ -20,17 +20,24 @@ Then, to enable notifications support add the following line at the top of your
#import "RNNotifications.h" #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 ```objective-c
// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
{ [RNNotifications startMonitorNotifications]; // -> Add this line
[RNNotifications didRegisterUserNotificationSettings:notificationSettings];
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]; [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
} }
...@@ -38,16 +45,6 @@ And the following methods to support registration and receiving notifications: ...@@ -38,16 +45,6 @@ And the following methods to support registration and receiving notifications:
[RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; [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];
}
``` ```
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android ## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
......
...@@ -10,9 +10,9 @@ Example: ...@@ -10,9 +10,9 @@ Example:
```javascript ```javascript
let localNotification = NotificationsIOS.localNotification({ let localNotification = NotificationsIOS.localNotification({
alertBody: "Local notificiation!", body: "Local notificiation!",
alertTitle: "Local Notification Title", title: "Local Notification Title",
soundName: "chime.aiff", sound: "chime.aiff",
silent: false, silent: false,
category: "SOME_CATEGORY", category: "SOME_CATEGORY",
userInfo: { } userInfo: { }
...@@ -22,10 +22,10 @@ let localNotification = NotificationsIOS.localNotification({ ...@@ -22,10 +22,10 @@ let localNotification = NotificationsIOS.localNotification({
Notification object contains: Notification object contains:
- **`fireDate`**- The date and time when the system should deliver the notification (optinal - default is immidiate dispatch). - **`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. - `body`- The message displayed in the notification alert.
- `alertTitle`- The title of the notification, displayed in the notifications center. - `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. - `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). - `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). - `category`- The category of this notification, required for [interactive notifications](#interactive--actionable-notifications-ios-only) (optional).
- `userInfo`- An optional object containing additional notification data. - `userInfo`- An optional object containing additional notification data.
...@@ -38,9 +38,9 @@ Example: ...@@ -38,9 +38,9 @@ Example:
```javascript ```javascript
let someLocalNotification = NotificationsIOS.localNotification({ let someLocalNotification = NotificationsIOS.localNotification({
alertBody: "Local notificiation!", body: "Local notificiation!",
alertTitle: "Local Notification Title", title: "Local Notification Title",
soundName: "chime.aiff", sound: "chime.aiff",
category: "SOME_CATEGORY", category: "SOME_CATEGORY",
userInfo: { } userInfo: { }
}); });
......
...@@ -5,40 +5,35 @@ ...@@ -5,40 +5,35 @@
When a push notification is received by the device, the application can be in one of the following states: 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. 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.
2. **Background:** When the app is running in a background state; in this case, a `notificationReceivedBackground` event will be fired.
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: Example:
```javascript ```javascript
constructor() { constructor() {
this._boundOnNotificationReceivedForeground = this.onNotificationReceivedForeground.bind(this); this._boundOnNotificationReceivedForeground = this.onNotificationReceivedForeground.bind(this);
this._boundOnNotificationReceivedBackground = this.onNotificationReceivedBackground.bind(this);
this._boundOnNotificationOpened = this.onNotificationOpened.bind(this); this._boundOnNotificationOpened = this.onNotificationOpened.bind(this);
NotificationsIOS.addEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground); NotificationsIOS.addEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground);
NotificationsIOS.addEventListener('notificationReceivedBackground', this._boundOnNotificationReceivedBackground);
NotificationsIOS.addEventListener('notificationOpened', this._boundOnNotificationOpened); NotificationsIOS.addEventListener('notificationOpened', this._boundOnNotificationOpened);
} }
onNotificationReceivedForeground(notification) { onNotificationReceivedForeground(notification, completion) {
completion({alert: true, sound: false, badge: false});
console.log("Notification Received - Foreground", notification); console.log("Notification Received - Foreground", notification);
} }
onNotificationReceivedBackground(notification) { onNotificationOpened(notification, completion, action) {
console.log("Notification Received - Background", notification);
}
onNotificationOpened(notification) {
console.log("Notification opened by device user", notification); 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() { componentWillUnmount() {
// Don't forget to remove the event listeners to prevent memory leaks! // Don't forget to remove the event listeners to prevent memory leaks!
NotificationsIOS.removeEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground); NotificationsIOS.removeEventListener('notificationReceivedForeground', this._boundOnNotificationReceivedForeground);
NotificationsIOS.removeEventListener('notificationReceivedBackground', this._boundOnNotificationReceivedBackground);
NotificationsIOS.removeEventListener('notificationOpened', this._boundOnNotificationOpened); NotificationsIOS.removeEventListener('notificationOpened', this._boundOnNotificationOpened);
} }
``` ```
...@@ -54,12 +49,6 @@ When you receive a push notification, you'll get an instance of `IOSNotification ...@@ -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. - **`getData()`**- returns the data payload (additional info) of the notification.
- **`getType()`**- returns `managed` for managed notifications, otherwise returns `regular`. - **`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.
## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android ## <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/APK_format_icon.png/768px-APK_format_icon.png" width=30/> Android
......
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
}
};
}
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))
};
{
"setupTestFrameworkScriptFile" : "./init.js",
"testEnvironment": "node",
"bail": true,
"verbose": true
}
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`);
}
}
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
import { import {
AppRegistry, AppRegistry,
StyleSheet, StyleSheet,
View,
Text, Text,
View Button
} from 'react-native'; } from 'react-native';
import React, {Component} from 'react'; import React, {Component} from 'react';
import NotificationsIOS, { NotificationAction, NotificationCategory } from 'react-native-notifications'; import NotificationsIOS, { NotificationAction, NotificationCategory } from 'react-native-notifications';
let upvoteAction = new NotificationAction({ let upvoteAction = new NotificationAction({
activationMode: "background", activationMode: 'background',
title: String.fromCodePoint(0x1F44D), title: String.fromCodePoint(0x1F44D),
identifier: "UPVOTE_ACTION" identifier: 'UPVOTE_ACTION'
}, (action, completed) => {
NotificationsIOS.log("ACTION RECEIVED");
NotificationsIOS.log(JSON.stringify(action));
completed();
}); });
let replyAction = new NotificationAction({ let replyAction = new NotificationAction({
activationMode: "background", activationMode: 'background',
title: "Reply", title: 'Reply',
behavior: "textInput",
authenticationRequired: true, authenticationRequired: true,
identifier: "REPLY_ACTION" textInput: {
}, (action, completed) => { buttonTitle: 'Reply now',
console.log("ACTION RECEIVED"); placeholder: 'Insert message'
console.log(action); },
identifier: 'REPLY_ACTION'
completed();
});
let cat = new NotificationCategory({
identifier: "SOME_CATEGORY",
actions: [upvoteAction, replyAction],
context: "default"
}); });
class NotificationsExampleApp extends Component { class NotificationsExampleApp extends Component {
constructor() { constructor() {
super(); super();
NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); this.state = {
NotificationsIOS.requestPermissions([cat]); 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.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this));
NotificationsIOS.registerPushKit(); NotificationsIOS.registerPushKit();
NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this));
NotificationsIOS.addEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this));
NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.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) { onPushRegistered(deviceToken) {
console.log("Device Token Received: " + deviceToken); console.log('Device Token Received: ' + deviceToken);
} }
onPushKitRegistered(deviceToken) { onPushRegisteredFailed(error) {
console.log("PushKit Token Received: " + deviceToken); console.log('Remote notifiction registration failed: ' + error);
} }
onNotificationReceivedForeground(notification) { onPushKitRegistered(deviceToken) {
console.log("Notification Received Foreground: " + JSON.stringify(notification)); console.log('PushKit Token Received: ' + deviceToken);
} }
onNotificationReceivedBackground(notification) { onPushKitNotificationReceived(notification) {
NotificationsIOS.log("Notification Received Background: " + JSON.stringify(notification)); console.log('PushKit notification Received: ' + JSON.stringify(notification));
}
let localNotification = NotificationsIOS.localNotification({ onNotificationReceivedForeground(notification, completion) {
alertBody: "Received background notificiation!", console.log('Notification Received Foreground with title: ' + JSON.stringify(notification));
alertTitle: "Local Notification Title", this.setState({
alertAction: "Click here to open", notifications: [...this.state.notifications, notification.getData().link]
soundName: "chime.aiff",
category: "SOME_CATEGORY",
userInfo: notification.getData()
}); });
// if you want to fire the local notification 10 seconds later, completion({alert: notification.getData().showAlert, sound: false, badge: false});
// 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));
// 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) { renderNotification(notification) {
console.log("Notification Opened: " + JSON.stringify(notification)); return <Text>{`${notification}`}</Text>;
} }
render() { render() {
const notifications = this.state.notifications.map((notification, idx) =>
(
<View key={`notification_${idx}`}>
{this.renderNotification(notification)}
</View>
));
return ( return (
<View style={styles.container}> <View style={styles.container}>
<Text style={styles.welcome}> <Button title={'Request permissions'} onPress={this.requestPermissions} testID={'requestPermissions'}/>
Welcome to React Native Notifications Demo App! <Button title={'Send local notification'} onPress={this.sendLocalNotification} testID={'sendLocalNotification'}/>
</Text> <Button title={'Remove all delivered notifications'} onPress={this.removeAllDeliveredNotifications}/>
<Text style={styles.instructions}> {notifications}
To get started, edit index.ios.js
</Text>
<Text style={styles.instructions}>
Press Cmd+R to reload,{'\n'}
Cmd+D or shake for dev menu
</Text>
</View> </View>
); );
} }
requestPermissions() {
let cat = new NotificationCategory({
identifier: 'SOME_CATEGORY',
actions: [upvoteAction, replyAction]
});
NotificationsIOS.requestPermissions([cat]);
}
sendLocalNotification() {
NotificationsIOS.localNotification({
body: 'Local notificiation!',
title: 'Local Notification Title',
sound: 'chime.aiff',
category: 'SOME_CATEGORY',
userInfo: { }
});
}
removeAllDeliveredNotifications() {
NotificationsIOS.removeAllDeliveredNotifications();
}
componentWillUnmount() { componentWillUnmount() {
NotificationsIOS.removeEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this)); NotificationsIOS.removeEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this));
NotificationsIOS.removeEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this));
NotificationsIOS.removeEventListener('notificationOpened', this.onNotificationOpened.bind(this)); NotificationsIOS.removeEventListener('notificationOpened', this.onNotificationOpened.bind(this));
NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this)); NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
NotificationsIOS.removeEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this)); NotificationsIOS.removeEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this));
// NotificationsIOS.resetCategories();
}
_onNotification(notification) {
AlertIOS.alert(
'Notification Received',
'Alert message: ' + notification.getMessage(),
[{
text: 'Dismiss',
onPress: null,
}]
);
} }
} }
......
...@@ -6,6 +6,20 @@ ...@@ -6,6 +6,20 @@
parallelizeBuildables = "NO" parallelizeBuildables = "NO"
buildImplicitDependencies = "YES"> buildImplicitDependencies = "YES">
<BuildActionEntries> <BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "3D3C04B91DE3340900C268FA"
BuildableName = "libyoga.a"
BlueprintName = "yoga"
ReferencedContainer = "container:../../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry <BuildActionEntry
buildForTesting = "YES" buildForTesting = "YES"
buildForRunning = "YES" buildForRunning = "YES"
...@@ -17,7 +31,21 @@ ...@@ -17,7 +31,21 @@
BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192" BlueprintIdentifier = "83CBBA2D1A601D0E00E9B192"
BuildableName = "libReact.a" BuildableName = "libReact.a"
BlueprintName = "React" BlueprintName = "React"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj"> ReferencedContainer = "container:../../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "58B511DA1A9E6C8500147676"
BuildableName = "libRNNotifications.a"
BlueprintName = "RNNotifications"
ReferencedContainer = "container:../../RNNotifications/RNNotifications.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
<BuildActionEntry <BuildActionEntry
...@@ -40,7 +68,6 @@ ...@@ -40,7 +68,6 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
</Testables> </Testables>
...@@ -60,7 +87,6 @@ ...@@ -60,7 +87,6 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
......
...@@ -9,9 +9,7 @@ ...@@ -9,9 +9,7 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import <PushKit/PushKit.h> @interface AppDelegate : UIResponder <UIApplicationDelegate>
@interface AppDelegate : UIResponder <UIApplicationDelegate, PKPushRegistryDelegate>
@property (nonatomic, strong) UIWindow *window; @property (nonatomic, strong) UIWindow *window;
......
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "AppDelegate.h" #import "AppDelegate.h"
#import "RCTBundleURLProvider.h" #import <React/RCTBundleURLProvider.h>
#import "RCTRootView.h" #import <React/RCTRootView.h>
#import "RNNotifications.h" #import <RNNotifications/RNNotifications.h>
#import <PushKit/PushKit.h> #import <PushKit/PushKit.h>
@implementation AppDelegate @implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
{
NSURL *jsCodeLocation; NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"NotificationsExampleApp" moduleName:@"NotificationsExampleApp"
...@@ -34,54 +24,18 @@ ...@@ -34,54 +24,18 @@
rootViewController.view = rootView; rootViewController.view = rootView;
self.window.rootViewController = rootViewController; self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible]; [self.window makeKeyAndVisible];
[RNNotifications startMonitorNotifications];
return YES; return YES;
} }
// PushKit API Example - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
- (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];
}
// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
[RNNotifications didRegisterUserNotificationSettings:notificationSettings];
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
} }
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
// Required for the notification event. [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error];
- (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];
}
// 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];
} }
@end @end
{ {
"images" : [ "images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"scale" : "3x"
},
{ {
"idiom" : "iphone", "idiom" : "iphone",
"size" : "29x29", "size" : "29x29",
...@@ -29,6 +39,11 @@ ...@@ -29,6 +39,11 @@
"idiom" : "iphone", "idiom" : "iphone",
"size" : "60x60", "size" : "60x60",
"scale" : "3x" "scale" : "3x"
},
{
"idiom" : "ios-marketing",
"size" : "1024x1024",
"scale" : "1x"
} }
], ],
"info" : { "info" : {
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>
{
"name": "NotificationsExampleApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
},
"dependencies": {
"react": "16.8.3",
"react-native": "0.59.x",
"react-native-notifications": "latest"
}
}
require('./example/index');
\ No newline at end of file
import {NativeModules, DeviceEventEmitter} from "react-native"; import {NativeModules, DeviceEventEmitter} from 'react-native';
import NotificationAndroid from "./notification"; import NotificationAndroid from './notification';
const RNNotifications = NativeModules.WixRNNotifications; const RNNotifications = NativeModules.WixRNNotifications;
...@@ -10,7 +10,7 @@ let registrationTokenUpdateListener; ...@@ -10,7 +10,7 @@ let registrationTokenUpdateListener;
export class NotificationsAndroid { export class NotificationsAndroid {
static setNotificationOpenedListener(listener) { static setNotificationOpenedListener(listener) {
notificationOpenedListener = DeviceEventEmitter.addListener("notificationOpened", (notification) => listener(new NotificationAndroid(notification))); notificationOpenedListener = DeviceEventEmitter.addListener('notificationOpened', (notification) => listener(new NotificationAndroid(notification)));
} }
static clearNotificationOpenedListener() { static clearNotificationOpenedListener() {
...@@ -21,11 +21,11 @@ export class NotificationsAndroid { ...@@ -21,11 +21,11 @@ export class NotificationsAndroid {
} }
static setNotificationReceivedListener(listener) { static setNotificationReceivedListener(listener) {
notificationReceivedListener = DeviceEventEmitter.addListener("notificationReceived", (notification) => listener(new NotificationAndroid(notification))); notificationReceivedListener = DeviceEventEmitter.addListener('notificationReceived', (notification) => listener(new NotificationAndroid(notification)));
} }
static setNotificationReceivedInForegroundListener(listener) { static setNotificationReceivedInForegroundListener(listener) {
notificationReceivedInForegroundListener = DeviceEventEmitter.addListener("notificationReceivedInForeground", (notification) => listener(new NotificationAndroid(notification))); notificationReceivedInForegroundListener = DeviceEventEmitter.addListener('notificationReceivedInForeground', (notification) => listener(new NotificationAndroid(notification)));
} }
static clearNotificationReceivedListener() { static clearNotificationReceivedListener() {
...@@ -43,7 +43,7 @@ export class NotificationsAndroid { ...@@ -43,7 +43,7 @@ export class NotificationsAndroid {
} }
static setRegistrationTokenUpdateListener(listener) { static setRegistrationTokenUpdateListener(listener) {
registrationTokenUpdateListener = DeviceEventEmitter.addListener("remoteNotificationsRegistered", listener); registrationTokenUpdateListener = DeviceEventEmitter.addListener('remoteNotificationsRegistered', listener);
} }
static clearRegistrationTokenUpdateListener() { static clearRegistrationTokenUpdateListener() {
......
/** /**
* @flow * @flow
*/ */
"use strict"; 'use strict';
import { NativeModules, DeviceEventEmitter, NativeAppEventEmitter } from "react-native"; import { NativeModules, DeviceEventEmitter } from 'react-native';
import Map from "core-js/library/es6/map"; import Map from 'core-js/library/es6/map';
import uuid from "uuid"; import uuid from 'uuid';
const NativeRNNotifications = NativeModules.RNNotifications; // eslint-disable-line no-unused-vars const NativeRNNotifications = NativeModules.RNBridgeModule; // eslint-disable-line no-unused-vars
import IOSNotification from "./notification.ios"; import IOSNotification from './notification.ios';
export const DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT = "remoteNotificationsRegistered"; export const DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT = 'remoteNotificationsRegistered';
export const DEVICE_REMOTE_NOTIFICATIONS_REGISTRATION_FAILED_EVENT = "remoteNotificationsRegistrationFailed"; export const DEVICE_REMOTE_NOTIFICATIONS_REGISTRATION_FAILED_EVENT = 'remoteNotificationsRegistrationFailed';
export const DEVICE_PUSH_KIT_REGISTERED_EVENT = "pushKitRegistered"; export const DEVICE_PUSH_KIT_REGISTERED_EVENT = 'pushKitRegistered';
export const DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT = "notificationReceivedForeground"; export const DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT = 'notificationReceivedForeground';
export const DEVICE_NOTIFICATION_RECEIVED_BACKGROUND_EVENT = "notificationReceivedBackground"; export const DEVICE_NOTIFICATION_OPENED_EVENT = 'notificationOpened';
export const DEVICE_NOTIFICATION_OPENED_EVENT = "notificationOpened"; export const DEVICE_PUSH_KIT_NOTIFICATION_RECEIVED_EVENT = 'pushKitNotificationReceived';
const DEVICE_NOTIFICATION_ACTION_RECEIVED = "notificationActionReceived";
const _exportedEvents = [ const _exportedEvents = [
DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT, DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT,
DEVICE_REMOTE_NOTIFICATIONS_REGISTRATION_FAILED_EVENT, DEVICE_REMOTE_NOTIFICATIONS_REGISTRATION_FAILED_EVENT,
DEVICE_PUSH_KIT_REGISTERED_EVENT, DEVICE_PUSH_KIT_REGISTERED_EVENT,
DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT, DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT,
DEVICE_NOTIFICATION_RECEIVED_BACKGROUND_EVENT, DEVICE_NOTIFICATION_OPENED_EVENT,
DEVICE_NOTIFICATION_OPENED_EVENT DEVICE_PUSH_KIT_NOTIFICATION_RECEIVED_EVENT
]; ];
const _notificationHandlers = new Map(); const _notificationHandlers = new Map();
const _actionHandlers = new Map(); const _actionHandlers = new Map();
let _actionListener;
export class NotificationAction { export class NotificationAction {
options: Object; options: Object;
handler: Function;
constructor(options: Object, handler: Function) { constructor(options: Object) {
this.options = options; this.options = options;
this.handler = handler;
} }
} }
...@@ -56,13 +52,13 @@ export default class NotificationsIOS { ...@@ -56,13 +52,13 @@ export default class NotificationsIOS {
* *
* - `remoteNotificationsRegistered` : Fired when the user registers for remote notifications. The handler will be invoked with a hex string representing the deviceToken. * - `remoteNotificationsRegistered` : Fired when the user registers for remote notifications. The handler will be invoked with a hex string representing the deviceToken.
* - `notificationReceivedForeground` : Fired when a notification (local / remote) is received when app is on foreground state. * - `notificationReceivedForeground` : Fired when a notification (local / remote) is received when app is on foreground state.
* - `notificationReceivedBackground`: Fired when a background notification is received.
* - `notificationOpened`: Fired when a notification (local / remote) is opened. * - `notificationOpened`: Fired when a notification (local / remote) is opened.
* - `pushKitNotificationReceived` : Fired when a pushKit notification received when app is both on foreground and background state.
*/ */
static addEventListener(type: string, handler: Function) { static addEventListener(type: string, handler: Function) {
if (_exportedEvents.indexOf(type) !== -1) { if (_exportedEvents.indexOf(type) !== -1) {
let listener; let listener;
if (type === DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT) { if (type === DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT) {
listener = DeviceEventEmitter.addListener( listener = DeviceEventEmitter.addListener(
DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT, DEVICE_REMOTE_NOTIFICATIONS_REGISTERED_EVENT,
...@@ -78,10 +74,24 @@ export default class NotificationsIOS { ...@@ -78,10 +74,24 @@ export default class NotificationsIOS {
DEVICE_PUSH_KIT_REGISTERED_EVENT, DEVICE_PUSH_KIT_REGISTERED_EVENT,
registration => handler(registration.pushKitToken) registration => handler(registration.pushKitToken)
); );
} else { } else if (type === DEVICE_NOTIFICATION_RECEIVED_FOREGROUND_EVENT) {
listener = DeviceEventEmitter.addListener(
type,
({payload, identifier}) => handler(new IOSNotification(payload), (presentingOptions) => {
NativeRNNotifications.finishPresentingNotification(identifier, presentingOptions);
})
);
} else if (type === DEVICE_NOTIFICATION_OPENED_EVENT) {
listener = DeviceEventEmitter.addListener(
type,
({payload, identifier, action}) => handler(new IOSNotification(payload), () => {
NativeRNNotifications.finishHandlingAction(identifier);
}, action)
);
} else if (type === DEVICE_PUSH_KIT_NOTIFICATION_RECEIVED_EVENT) {
listener = DeviceEventEmitter.addListener( listener = DeviceEventEmitter.addListener(
type, type,
notification => handler(new IOSNotification(notification)) (payload) => handler(new IOSNotification(payload))
); );
} }
...@@ -104,18 +114,6 @@ export default class NotificationsIOS { ...@@ -104,18 +114,6 @@ export default class NotificationsIOS {
} }
} }
static _actionHandlerDispatcher(action: Object) {
const actionHandler = _actionHandlers.get(action.identifier);
if (actionHandler) {
action.notification = new IOSNotification(action.notification);
actionHandler(action, () => {
NativeRNNotifications.completionHandler(action.completionKey);
});
}
}
/** /**
* Sets the notification categories * Sets the notification categories
*/ */
...@@ -123,9 +121,6 @@ export default class NotificationsIOS { ...@@ -123,9 +121,6 @@ export default class NotificationsIOS {
let notificationCategories = []; let notificationCategories = [];
if (categories) { if (categories) {
// subscribe once for all actions
_actionListener = NativeAppEventEmitter.addListener(DEVICE_NOTIFICATION_ACTION_RECEIVED, this._actionHandlerDispatcher.bind(this));
notificationCategories = categories.map(category => { notificationCategories = categories.map(category => {
return Object.assign({}, category.options, { return Object.assign({}, category.options, {
actions: category.options.actions.map(action => { actions: category.options.actions.map(action => {
...@@ -153,10 +148,6 @@ export default class NotificationsIOS { ...@@ -153,10 +148,6 @@ export default class NotificationsIOS {
* memory leaks * memory leaks
*/ */
static resetCategories() { static resetCategories() {
if (_actionListener) {
_actionListener.remove();
}
_actionHandlers.clear(); _actionHandlers.clear();
} }
...@@ -176,10 +167,6 @@ export default class NotificationsIOS { ...@@ -176,10 +167,6 @@ export default class NotificationsIOS {
NativeRNNotifications.backgroundTimeRemaining(callback); NativeRNNotifications.backgroundTimeRemaining(callback);
} }
static consumeBackgroundQueue() {
NativeRNNotifications.consumeBackgroundQueue();
}
static log(message: string) { static log(message: string) {
NativeRNNotifications.log(message); NativeRNNotifications.log(message);
} }
...@@ -198,10 +185,10 @@ export default class NotificationsIOS { ...@@ -198,10 +185,10 @@ export default class NotificationsIOS {
* *
* notification is an object containing: * notification is an object containing:
* *
* - `alertBody` : The message displayed in the notification alert. * - `body` : The message displayed in the notification alert.
* - `alertTitle` : The message title displayed in the notification. * - `title` : The message title displayed in the notification.
* - `alertAction` : The "action" displayed beneath an actionable notification. Defaults to "view"; * - `alertAction` : The 'action' displayed beneath an actionable notification. Defaults to 'view';
* - `soundName` : The sound played when the notification is fired (optional). * - `sound` : The sound played when the notification is fired (optional).
* - `silent` : If true, the notification sound will be suppressed (optional). * - `silent` : If true, the notification sound will be suppressed (optional).
* - `category` : The category of this notification, required for actionable notifications (optional). * - `category` : The category of this notification, required for actionable notifications (optional).
* - `userInfo` : An optional object containing additional notification data. * - `userInfo` : An optional object containing additional notification data.
...@@ -254,8 +241,8 @@ export default class NotificationsIOS { ...@@ -254,8 +241,8 @@ export default class NotificationsIOS {
* A delivered notification is an object containing: * A delivered notification is an object containing:
* *
* - `identifier` : The identifier of this notification. * - `identifier` : The identifier of this notification.
* - `alertBody` : The message displayed in the notification alert. * - `body` : The message displayed in the notification alert.
* - `alertTitle` : The message title displayed in the notification. * - `title` : The message title displayed in the notification.
* - `category` : The category of this notification, if has one. * - `category` : The category of this notification, if has one.
* - `userInfo` : An optional object containing additional notification data. * - `userInfo` : An optional object containing additional notification data.
* - `thread-id` : The thread identifier of this notification, if has one. * - `thread-id` : The thread identifier of this notification, if has one.
......
...@@ -11,8 +11,8 @@ export default class IOSNotification { ...@@ -11,8 +11,8 @@ export default class IOSNotification {
this._data = {}; this._data = {};
if (notification.aps && if (notification.aps &&
notification.aps["content-available"] && notification.aps['content-available'] &&
notification.aps["content-available"] === 1 && notification.aps['content-available'] === 1 &&
!notification.aps.alert && !notification.aps.alert &&
!notification.aps.sound && !notification.aps.sound &&
notification.managedAps) { notification.managedAps) {
...@@ -21,8 +21,8 @@ export default class IOSNotification { ...@@ -21,8 +21,8 @@ export default class IOSNotification {
this._sound = notification.managedAps.sound; this._sound = notification.managedAps.sound;
this._badge = notification.aps.badge; this._badge = notification.aps.badge;
this._category = notification.managedAps.category; this._category = notification.managedAps.category;
this._type = "managed"; this._type = 'managed';
this._thread = notification.aps["thread-id"]; this._thread = notification.aps.thread;
} else if ( } else if (
notification.aps && notification.aps &&
notification.aps.alert) { notification.aps.alert) {
...@@ -31,11 +31,11 @@ export default class IOSNotification { ...@@ -31,11 +31,11 @@ export default class IOSNotification {
this._sound = notification.aps.sound; this._sound = notification.aps.sound;
this._badge = notification.aps.badge; this._badge = notification.aps.badge;
this._category = notification.aps.category; this._category = notification.aps.category;
this._type = "regular"; this._type = 'regular';
this._thread = notification.aps["thread-id"]; this._thread = notification.aps.thread;
} }
Object.keys(notification).filter(key => key !== "aps").forEach(key => { Object.keys(notification).filter(key => key !== 'aps').forEach(key => {
this._data[key] = notification[key]; this._data[key] = notification[key];
}); });
} }
......
module.exports = {
projectRoot: `${__dirname}/example`,
watchFolders: [
__dirname,
],
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
})
}
};
{ {
"name": "react-native-notifications", "name": "react-native-notifications",
"version": "1.5.0", "version": "2.0.0",
"description": "Advanced Push Notifications (Silent, interactive notifications) for iOS & Android", "description": "Advanced Push Notifications (Silent, interactive notifications) for iOS & Android",
"author": "Lidan Hifi <lidan.hifi@gmail.com>", "author": "Lidan Hifi <lidan.hifi@gmail.com>",
"license": "MIT", "license": "MIT",
...@@ -19,6 +19,13 @@ ...@@ -19,6 +19,13 @@
"actionable-notifications", "actionable-notifications",
"interactive-notifications" "interactive-notifications"
], ],
"main": "lib/src/index",
"scripts": {
"pretest": "./node_modules/.bin/eslint *.js test",
"test": "jest",
"start": "node ./scripts/start",
"test-e2e-ios": "node ./scripts/test-e2e --ios"
},
"nativePackage": true, "nativePackage": true,
"dependencies": { "dependencies": {
"core-js": "^1.0.0", "core-js": "^1.0.0",
...@@ -29,21 +36,27 @@ ...@@ -29,21 +36,27 @@
"react-native": ">=0.25.1" "react-native": ">=0.25.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "16.x.x",
"@types/react-native": "0.57.7",
"@types/react-test-renderer": "16.x.x",
"@babel/plugin-proposal-export-default-from": "7.2.0",
"@babel/plugin-proposal-export-namespace-from": "7.2.0",
"typescript": "3.2.2",
"babel-eslint": "9.0.0", "babel-eslint": "9.0.0",
"babel-preset-react-native": "^1.9.0",
"babel-register": "^6.7.2",
"chai": "^3.5.0", "chai": "^3.5.0",
"chokidar-cli": "^1.2.0", "chokidar-cli": "^1.2.0",
"eslint": "5.1.x", "eslint": "6.0.1",
"mocha": "^2.5.3", "mocha": "^2.5.3",
"proxyquire": "^1.7.4", "proxyquire": "^1.7.4",
"sinon": "^1.17.3", "sinon": "^1.17.3",
"sinon-chai": "^2.8.0" "sinon-chai": "^2.8.0",
}, "shell-utils": "1.x.x",
"scripts": { "react-native": "0.60.0",
"pretest": "./node_modules/.bin/eslint *.js test", "react": "16.8.6",
"test": "./node_modules/.bin/mocha --compilers js:babel-register --reporter spec \"test/*.spec.js\"", "detox": "13.x.x",
"start": "npm run test --silent; ./node_modules/.bin/chokidar \"test/*.js\" \"*.js\" -c 'npm run test --silent' --silent" "jest": "24.8.0",
"metro-react-native-babel-preset": "0.55.x",
"@babel/register": "7.4.4"
}, },
"publishConfig": { "publishConfig": {
"registry": "https://registry.npmjs.org/" "registry": "https://registry.npmjs.org/"
...@@ -56,9 +69,60 @@ ...@@ -56,9 +69,60 @@
"bugs": { "bugs": {
"url": "https://github.com/wix/react-native-notifications/issues" "url": "https://github.com/wix/react-native-notifications/issues"
}, },
"babel": { "detox": {
"presets": [ "test-runner": "jest",
"react-native" "specs": "",
"configurations": {
"ios.none": {
"binaryPath": "playground/ios/DerivedData/playground/Build/Products/Debug-iphonesimulator/playground.app",
"type": "ios.none",
"name": "iPhone X",
"session": {
"server": "ws://localhost:8099",
"sessionId": "playground"
}
},
"ios.sim.debug": {
"binaryPath": "example/ios/DerivedData/NotificationsExampleApp/Build/Products/Debug-iphonesimulator/NotificationsExampleApp.app",
"build": "RCT_NO_LAUNCH_PACKAGER=true xcodebuild build -scheme NotificationsExampleApp -project example/ios/NotificationsExampleApp.xcodeproj -sdk iphonesimulator -configuration Debug -derivedDataPath example/ios/DerivedData/NotificationsExampleApp ONLY_ACTIVE_ARCH=YES -quiet -UseModernBuildSystem=NO",
"type": "ios.simulator",
"name": "iPhone X"
},
"ios.sim.release": {
"binaryPath": "playground/ios/DerivedData/playground/Build/Products/Release-iphonesimulator/playground.app",
"build": "RCT_NO_LAUNCH_PACKAGER=true xcodebuild build -scheme playground_release -project playground/ios/playground.xcodeproj -sdk iphonesimulator -configuration Release -derivedDataPath playground/ios/DerivedData/playground ONLY_ACTIVE_ARCH=YES -quiet -UseModernBuildSystem=NO",
"type": "ios.simulator",
"name": "iPhone X"
}
}
},
"jest": {
"preset": "react-native",
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js"
},
"roots": [
"<rootDir>/node_modules/",
"<rootDir>/test/"
],
"collectCoverageFrom": [
"lib/src/**/*.js",
"integration/**/*.js",
"!lib/dist/index.js",
"!lib/dist/Navigation.js",
"!lib/dist/adapters/**/*",
"!lib/dist/interfaces/**/*",
"!lib/dist/**/*.test.*",
"!integration/**/*.test.*",
"!integration/*.test.*"
],
"resetMocks": true,
"resetModules": true,
"coverageReporters": [
"json",
"lcov",
"text",
"html"
] ]
} }
} }
const exec = require('shell-utils').exec;
run();
function run() {
exec.killPort(8081);
exec.execSync(`watchman watch-del-all || true`);
exec.execSync(`adb reverse tcp:8081 tcp:8081 || true`);
exec.execSync(`node ./node_modules/react-native/local-cli/cli.js start`);
}
const _ = require('lodash');
const exec = require('shell-utils').exec;
const android = _.includes(process.argv, '--android');
const release = _.includes(process.argv, '--release');
const skipBuild = _.includes(process.argv, '--skipBuild');
const headless = _.includes(process.argv, '--headless');
const multi = _.includes(process.argv, '--multi');
run();
function run() {
const prefix = android ? `android.emu` : `ios.sim`;
const suffix = release ? `release` : `debug`;
const configuration = `${prefix}.${suffix}`;
const headless$ = android ? headless ? `--headless` : `` : ``;
const workers = multi ? 3 : 1;
if (!skipBuild) {
exec.execSync(`detox build --configuration ${configuration}`);
}
exec.execSync(`detox test --loglevel verbose --configuration ${configuration} ${headless$} ${!android ? `-w ${workers}` : ``}`); //-f "ScreenStyle.test.js" --loglevel trace
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
const babelOptions = require('./package.json').babel;
module.exports = function (wallaby) {
return {
env: {
type: 'node',
runner: 'node'
},
testFramework: 'jest',
files: [
'package.json',
'lib/src/**/*.js',
'lib/src/**/*.ts',
'lib/src/**/*.tsx'
],
tests: [
'test/**/*.spec.js'
],
compilers: {
'**/*.js': wallaby.compilers.babel(babelOptions),
'**/*.ts?(x)': wallaby.compilers.typeScript({
module: 'commonjs',
jsx: 'React'
})
},
setup: (w) => {
w.testFramework.configure(require('./package.json').jest);
}
};
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment