RNNotifications.m 28.6 KB
Newer Older
Lidan Hifi's avatar
Lidan Hifi committed
1 2

#import <UIKit/UIKit.h>
3
#import <PushKit/PushKit.h>
Gustavo Perdomo's avatar
Gustavo Perdomo committed
4
#if __has_include(<React/RCTBridge.h>)
5 6 7 8 9
  #import <React/RCTBridge.h>
  #import <React/RCTEventDispatcher.h>
  #import "RNNotifications.h"
  #import <React/RCTConvert.h>
  #import <React/RCTUtils.h>
Gustavo Perdomo's avatar
Gustavo Perdomo committed
10 11 12 13 14 15 16
#else
  #import "RCTBridge.h"
  #import "RCTEventDispatcher.h"
  #import "RNNotifications.h"
  #import "RCTConvert.h"
  #import "RCTUtils.h"
#endif
17
#import "RNNotificationsBridgeQueue.h"
18
#import <UserNotifications/UserNotifications.h>
Lidan Hifi's avatar
Lidan Hifi committed
19

20 21
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

22 23
NSString* const RNNotificationCreateAction = @"CREATE";
NSString* const RNNotificationClearAction = @"CLEAR";
Lidan Hifi's avatar
Lidan Hifi committed
24

25
NSString* const RNNotificationsRegistered = @"RNNotificationsRegistered";
26
NSString* const RNNotificationsRegistrationFailed = @"RNNotificationsRegistrationFailed";
27
NSString* const RNPushKitRegistered = @"RNPushKitRegistered";
28 29 30 31
NSString* const RNNotificationReceivedForeground = @"RNNotificationReceivedForeground";
NSString* const RNNotificationReceivedBackground = @"RNNotificationReceivedBackground";
NSString* const RNNotificationOpened = @"RNNotificationOpened";
NSString* const RNNotificationActionTriggered = @"RNNotificationActionTriggered";
Lidan Hifi's avatar
Lidan Hifi committed
32

33
/*
34
 * Converters for Interactive Notifications
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
 */
@implementation RCTConvert (UIUserNotificationActivationMode)
RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{
                                                        @"foreground": @(UIUserNotificationActivationModeForeground),
                                                        @"background": @(UIUserNotificationActivationModeBackground)
                                                        }), UIUserNotificationActivationModeForeground, integerValue)
@end

@implementation RCTConvert (UIUserNotificationActionContext)
RCT_ENUM_CONVERTER(UIUserNotificationActionContext, (@{
                                                       @"default": @(UIUserNotificationActionContextDefault),
                                                       @"minimal": @(UIUserNotificationActionContextMinimal)
                                                       }), UIUserNotificationActionContextDefault, integerValue)
@end

@implementation RCTConvert (UIUserNotificationActionBehavior)
/* iOS 9 only */
RCT_ENUM_CONVERTER(UIUserNotificationActionBehavior, (@{
                                                        @"default": @(UIUserNotificationActionBehaviorDefault),
                                                        @"textInput": @(UIUserNotificationActionBehaviorTextInput)
                                                        }), UIUserNotificationActionBehaviorDefault, integerValue)
@end

58 59 60 61
@implementation RCTConvert (UIMutableUserNotificationAction)
+ (UIMutableUserNotificationAction *)UIMutableUserNotificationAction:(id)json
{
    NSDictionary<NSString *, id> *details = [self NSDictionary:json];
Lidan Hifi's avatar
Lidan Hifi committed
62

63
    UIMutableUserNotificationAction* action =[UIMutableUserNotificationAction new];
64 65 66 67 68 69
    action.activationMode = [RCTConvert UIUserNotificationActivationMode:details[@"activationMode"]];
    action.behavior = [RCTConvert UIUserNotificationActionBehavior:details[@"behavior"]];
    action.authenticationRequired = [RCTConvert BOOL:details[@"authenticationRequired"]];
    action.destructive = [RCTConvert BOOL:details[@"destructive"]];
    action.title = [RCTConvert NSString:details[@"title"]];
    action.identifier = [RCTConvert NSString:details[@"identifier"]];
Lidan Hifi's avatar
Lidan Hifi committed
70

71 72 73
    return action;
}
@end
74

75 76
@implementation RCTConvert (UIMutableUserNotificationCategory)
+ (UIMutableUserNotificationCategory *)UIMutableUserNotificationCategory:(id)json
77
{
78 79
    NSDictionary<NSString *, id> *details = [self NSDictionary:json];

80
    UIMutableUserNotificationCategory* category = [UIMutableUserNotificationCategory new];
81 82 83
    category.identifier = details[@"identifier"];

    // category actions
84
    NSMutableArray* actions = [NSMutableArray new];
85 86
    for (NSDictionary* actionJson in [RCTConvert NSArray:details[@"actions"]]) {
        [actions addObject:[RCTConvert UIMutableUserNotificationAction:actionJson]];
87
    }
88 89 90 91

    [category setActions:actions forContext:[RCTConvert UIUserNotificationActionContext:details[@"context"]]];

    return category;
92
}
93 94
@end

95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
@implementation RCTConvert (UILocalNotification)
+ (UILocalNotification *)UILocalNotification:(id)json
{
    NSDictionary<NSString *, id> *details = [self NSDictionary:json];

    UILocalNotification* notification = [UILocalNotification new];
    notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]];
    notification.alertBody = [RCTConvert NSString:details[@"alertBody"]];
    notification.alertTitle = [RCTConvert NSString:details[@"alertTitle"]];
    notification.alertAction = [RCTConvert NSString:details[@"alertAction"]];
    notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName;
    notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
    notification.category = [RCTConvert NSString:details[@"category"]];

    return notification;
}
@end

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
@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[@"alertBody"]];
    content.title = [RCTConvert NSString:details[@"alertTitle"]];
    content.sound = [RCTConvert NSString:details[@"soundName"]]
        ? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"soundName"]]]
        : [UNNotificationSound defaultSound];
    content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
    content.categoryIdentifier = [RCTConvert NSString:details[@"category"]];

    NSDate *triggerDate = [RCTConvert NSDate:details[@"fireDate"]];
    UNCalendarNotificationTrigger *trigger = nil;
    if (triggerDate != nil) {
        NSDateComponents *triggerDateComponents = [[NSCalendar currentCalendar]
                                                   components:NSCalendarUnitYear +
                                                   NSCalendarUnitMonth + NSCalendarUnitDay +
                                                   NSCalendarUnitHour + NSCalendarUnitMinute +
                                                   NSCalendarUnitSecond + NSCalendarUnitTimeZone
                                                   fromDate:triggerDate];
        trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDateComponents
                                                                           repeats:NO];
    }

    return [UNNotificationRequest requestWithIdentifier:notificationId
                                                content:content trigger:trigger];
}
@end

145 146 147 148 149 150
static NSDictionary *RCTFormatUNNotification(UNNotification *notification)
{
  NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary];
  UNNotificationContent *content = notification.request.content;

  formattedNotification[@"identifier"] = notification.request.identifier;
151

152 153 154 155 156 157
  if (notification.date) {
    NSDateFormatter *formatter = [NSDateFormatter new];
    [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
    NSString *dateString = [formatter stringFromDate:notification.date];
    formattedNotification[@"fireDate"] = dateString;
  }
158

159 160 161 162 163
  formattedNotification[@"alertTitle"] = RCTNullIfNil(content.title);
  formattedNotification[@"alertBody"] = RCTNullIfNil(content.body);
  formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier);
  formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier);
  formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo));
164

165 166 167
  return formattedNotification;
}

168 169 170 171 172
@implementation RNNotifications

RCT_EXPORT_MODULE()

@synthesize bridge = _bridge;
Lidan Hifi's avatar
Lidan Hifi committed
173 174 175 176 177 178 179 180 181

- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)setBridge:(RCTBridge *)bridge
{
    _bridge = bridge;
Lidan Hifi's avatar
Lidan Hifi committed
182

183 184 185 186 187
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationsRegistered:)
                                                 name:RNNotificationsRegistered
                                               object:nil];

188 189 190 191 192
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationsRegistrationFailed:)
                                                 name:RNNotificationsRegistrationFailed
                                               object:nil];

193 194 195 196 197
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handlePushKitRegistered:)
                                                 name:RNPushKitRegistered
                                               object:nil];

Lidan Hifi's avatar
Lidan Hifi committed
198 199
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationReceivedForeground:)
Lidan Hifi's avatar
Lidan Hifi committed
200
                                                 name:RNNotificationReceivedForeground
Lidan Hifi's avatar
Lidan Hifi committed
201
                                               object:nil];
Lidan Hifi's avatar
Lidan Hifi committed
202

Lidan Hifi's avatar
Lidan Hifi committed
203 204
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationReceivedBackground:)
Lidan Hifi's avatar
Lidan Hifi committed
205
                                                 name:RNNotificationReceivedBackground
Lidan Hifi's avatar
Lidan Hifi committed
206
                                               object:nil];
Lidan Hifi's avatar
Lidan Hifi committed
207

Lidan Hifi's avatar
Lidan Hifi committed
208 209
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationOpened:)
Lidan Hifi's avatar
Lidan Hifi committed
210
                                                 name:RNNotificationOpened
Lidan Hifi's avatar
Lidan Hifi committed
211
                                               object:nil];
212 213 214 215 216

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationActionTriggered:)
                                                 name:RNNotificationActionTriggered
                                               object:nil];
217 218 219

    [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
Lidan Hifi's avatar
Lidan Hifi committed
220 221 222
}

/*
223
 * Public Methods
Lidan Hifi's avatar
Lidan Hifi committed
224
 */
225 226 227 228 229 230 231 232 233 234 235 236 237 238
+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
    if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) {
        [[UIApplication sharedApplication] registerForRemoteNotifications];
    }
}

+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistered
                                                        object:self
                                                      userInfo:@{@"deviceToken": [self deviceTokenToString:deviceToken]}];
}

239 240 241 242 243 244
+ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistrationFailed
                                                        object:self
                                                      userInfo:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}];
}

Lidan Hifi's avatar
Lidan Hifi committed
245 246 247
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification
{
    UIApplicationState state = [UIApplication sharedApplication].applicationState;
Lidan Hifi's avatar
Lidan Hifi committed
248

249 250 251 252 253 254 255 256 257 258 259 260 261
    if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) {
        // JS thread is ready, push the notification to the bridge

        if (state == UIApplicationStateActive) {
            // Notification received foreground
            [self didReceiveNotificationOnForegroundState:notification];
        } else if (state == UIApplicationStateInactive) {
            // Notification opened
            [self didNotificationOpen:notification];
        } else {
            // Notification received background
            [self didReceiveNotificationOnBackgroundState:notification];
        }
Lidan Hifi's avatar
Lidan Hifi committed
262
    } else {
263 264
        // JS thread is not ready - store it in the native notifications queue
        [[RNNotificationsBridgeQueue sharedInstance] postNotification:notification];
Lidan Hifi's avatar
Lidan Hifi committed
265 266 267 268 269 270
    }
}

+ (void)didReceiveLocalNotification:(UILocalNotification *)notification
{
    UIApplicationState state = [UIApplication sharedApplication].applicationState;
Lidan Hifi's avatar
Lidan Hifi committed
271

272 273 274 275
    NSMutableDictionary* newUserInfo = notification.userInfo.mutableCopy;
    [newUserInfo removeObjectForKey:@"__id"];
    notification.userInfo = newUserInfo;

276 277 278
    if (state == UIApplicationStateActive) {
        [self didReceiveNotificationOnForegroundState:notification.userInfo];
    } else if (state == UIApplicationStateInactive) {
Lidan Hifi's avatar
Lidan Hifi committed
279 280 281 282 283 284 285 286
        NSString* notificationId = [notification.userInfo objectForKey:@"notificationId"];
        if (notificationId) {
            [self clearNotificationFromNotificationsCenter:notificationId];
        }
        [self didNotificationOpen:notification.userInfo];
    }
}

287
+ (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
288
{
289
    [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:notification.userInfo completionHandler:completionHandler];
290 291
}

292
+ (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
293
{
294
    [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:userInfo completionHandler:completionHandler];
295 296
}

Lidan Hifi's avatar
Lidan Hifi committed
297 298 299 300 301
/*
 * Notification handlers
 */
+ (void)didReceiveNotificationOnForegroundState:(NSDictionary *)notification
{
Lidan Hifi's avatar
Lidan Hifi committed
302
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedForeground
Lidan Hifi's avatar
Lidan Hifi committed
303 304 305 306 307 308
                                                        object:self
                                                      userInfo:notification];
}

+ (void)didReceiveNotificationOnBackgroundState:(NSDictionary *)notification
{
309 310 311 312
    NSDictionary* managedAps  = [notification objectForKey:@"managedAps"];
    NSDictionary* alert = [managedAps objectForKey:@"alert"];
    NSString* action = [managedAps objectForKey:@"action"];
    NSString* notificationId = [managedAps objectForKey:@"notificationId"];
Lidan Hifi's avatar
Lidan Hifi committed
313 314 315

    if (action) {
        // create or delete notification
Lidan Hifi's avatar
Lidan Hifi committed
316
        if ([action isEqualToString: RNNotificationCreateAction]
Lidan Hifi's avatar
Lidan Hifi committed
317 318 319
            && notificationId
            && alert) {
            [self dispatchLocalNotificationFromNotification:notification];
Lidan Hifi's avatar
Lidan Hifi committed
320 321

        } else if ([action isEqualToString: RNNotificationClearAction] && notificationId) {
Lidan Hifi's avatar
Lidan Hifi committed
322 323 324
            [self clearNotificationFromNotificationsCenter:notificationId];
        }
    }
Lidan Hifi's avatar
Lidan Hifi committed
325

326 327 328
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedBackground
                                                        object:self
                                                      userInfo:notification];
Lidan Hifi's avatar
Lidan Hifi committed
329 330 331 332
}

+ (void)didNotificationOpen:(NSDictionary *)notification
{
Lidan Hifi's avatar
Lidan Hifi committed
333
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationOpened
Lidan Hifi's avatar
Lidan Hifi committed
334 335 336 337 338 339 340 341 342
                                                        object:self
                                                      userInfo:notification];
}

/*
 * Helper methods
 */
+ (void)dispatchLocalNotificationFromNotification:(NSDictionary *)notification
{
343 344 345 346
    NSDictionary* managedAps  = [notification objectForKey:@"managedAps"];
    NSDictionary* alert = [managedAps objectForKey:@"alert"];
    NSString* action = [managedAps objectForKey:@"action"];
    NSString* notificationId = [managedAps objectForKey:@"notificationId"];
Lidan Hifi's avatar
Lidan Hifi committed
347 348

    if ([action isEqualToString: RNNotificationCreateAction]
Lidan Hifi's avatar
Lidan Hifi committed
349 350
        && notificationId
        && alert) {
Lidan Hifi's avatar
Lidan Hifi committed
351

Lidan Hifi's avatar
Lidan Hifi committed
352
        // trigger new client push notification
353
        UILocalNotification* note = [UILocalNotification new];
Lidan Hifi's avatar
Lidan Hifi committed
354 355
        note.alertTitle = [alert objectForKey:@"title"];
        note.alertBody = [alert objectForKey:@"body"];
356
        note.userInfo = notification;
357
        note.soundName = [managedAps objectForKey:@"sound"];
358
        note.category = [managedAps objectForKey:@"category"];
Lidan Hifi's avatar
Lidan Hifi committed
359

Lidan Hifi's avatar
Lidan Hifi committed
360
        [[UIApplication sharedApplication] presentLocalNotificationNow:note];
Lidan Hifi's avatar
Lidan Hifi committed
361

Lidan Hifi's avatar
Lidan Hifi committed
362 363 364 365 366
        // Serialize it and store so we can delete it later
        NSData* data = [NSKeyedArchiver archivedDataWithRootObject:note];
        NSString* notificationKey = [self buildNotificationKeyfromNotification:notificationId];
        [[NSUserDefaults standardUserDefaults] setObject:data forKey:notificationKey];
        [[NSUserDefaults standardUserDefaults] synchronize];
Lidan Hifi's avatar
Lidan Hifi committed
367

Lidan Hifi's avatar
Lidan Hifi committed
368 369 370 371 372 373 374 375 376 377
        NSLog(@"Local notification was triggered: %@", notificationKey);
    }
}

+ (void)clearNotificationFromNotificationsCenter:(NSString *)notificationId
{
    NSString* notificationKey = [self buildNotificationKeyfromNotification:notificationId];
    NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:notificationKey];
    if (data) {
        UILocalNotification* notification = [NSKeyedUnarchiver unarchiveObjectWithData: data];
Lidan Hifi's avatar
Lidan Hifi committed
378

Lidan Hifi's avatar
Lidan Hifi committed
379 380 381
        // delete the notification
        [[UIApplication sharedApplication] cancelLocalNotification:notification];
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:notificationKey];
Lidan Hifi's avatar
Lidan Hifi committed
382

383 384
        NSLog(@"Local notification removed: %@", notificationKey);

Lidan Hifi's avatar
Lidan Hifi committed
385 386 387 388 389 390 391 392 393
        return;
    }
}

+ (NSString *)buildNotificationKeyfromNotification:(NSString *)notificationId
{
    return [NSString stringWithFormat:@"%@.%@", [[NSBundle mainBundle] bundleIdentifier], notificationId];
}

394 395 396 397 398 399 400 401 402 403 404 405
+ (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];
}

406
+ (void)requestPermissionsWithCategories:(NSMutableSet *)categories
407
{
408 409
    UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
    UIUserNotificationSettings* settings = [UIUserNotificationSettings settingsForTypes:types categories:categories];
410

411
    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
412 413
}

414
+ (void)emitNotificationActionForIdentifier:(NSString *)identifier responseInfo:(NSDictionary *)responseInfo userInfo:(NSDictionary *)userInfo  completionHandler:(void (^)())completionHandler
415
{
416 417
    NSString* completionKey = [NSString stringWithFormat:@"%@.%@", identifier, [NSString stringWithFormat:@"%d", (long)[[NSDate date] timeIntervalSince1970]]];
    NSMutableDictionary* info = [[NSMutableDictionary alloc] initWithDictionary:@{ @"identifier": identifier, @"completionKey": completionKey }];
418 419 420 421 422

    // add text
    NSString* text = [responseInfo objectForKey:UIUserNotificationActionResponseTypedTextKey];
    if (text != NULL) {
        info[@"text"] = text;
423 424
    }

425 426
    // add notification custom data
    if (userInfo != NULL) {
427
        info[@"notification"] = userInfo;
428
    }
429

430 431
    // Emit event to the queue (in order to store the completion handler). if JS thread is ready, post it also to the notification center (to the bridge).
    [[RNNotificationsBridgeQueue sharedInstance] postAction:info withCompletionKey:completionKey andCompletionHandler:completionHandler];
432

433 434 435 436 437
    if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) {
        [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered
                                                            object:self
                                                          userInfo:info];
    }
438 439
}

440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
+ (void)registerPushKit
{
    // Create a push registry object
    PKPushRegistry* pushKitRegistry = [[PKPushRegistry alloc] initWithQueue:dispatch_get_main_queue()];

    // Set the registry delegate to app delegate
    pushKitRegistry.delegate = [[UIApplication sharedApplication] delegate];
    pushKitRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}

+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
{
    [[NSNotificationCenter defaultCenter] postNotificationName:RNPushKitRegistered
                                                        object:self
                                                      userInfo:@{@"pushKitToken": [self deviceTokenToString:credentials.token]}];
}

- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
{
    [RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload];
}

462 463 464
/*
 * Javascript events
 */
465 466 467 468 469
- (void)handleNotificationsRegistered:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo];
}

470 471 472 473 474
- (void)handleNotificationsRegistrationFailed:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistrationFailed" body:notification.userInfo];
}

475 476 477 478 479
- (void)handlePushKitRegistered:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"pushKitRegistered" body:notification.userInfo];
}

Lidan Hifi's avatar
Lidan Hifi committed
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
- (void)handleNotificationReceivedForeground:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedForeground" body:notification.userInfo];
}

- (void)handleNotificationReceivedBackground:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationReceivedBackground" body:notification.userInfo];
}

- (void)handleNotificationOpened:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendDeviceEventWithName:@"notificationOpened" body:notification.userInfo];
}

495 496 497 498 499
- (void)handleNotificationActionTriggered:(NSNotification *)notification
{
    [_bridge.eventDispatcher sendAppEventWithName:@"notificationActionReceived" body:notification.userInfo];
}

Lidan Hifi's avatar
Lidan Hifi committed
500 501 502
/*
 * React Native exported methods
 */
503
RCT_EXPORT_METHOD(requestPermissionsWithCategories:(NSArray *)json)
Lidan Hifi's avatar
Lidan Hifi committed
504
{
505 506 507
    NSMutableSet* categories = nil;

    if ([json count] > 0) {
508
        categories = [NSMutableSet new];
509 510 511 512 513 514
        for (NSDictionary* categoryJson in json) {
            [categories addObject:[RCTConvert UIMutableUserNotificationCategory:categoryJson]];
        }
    }

    [RNNotifications requestPermissionsWithCategories:categories];
Lidan Hifi's avatar
Lidan Hifi committed
515 516
}

517 518 519 520 521
RCT_EXPORT_METHOD(log:(NSString *)message)
{
    NSLog(message);
}

522
RCT_EXPORT_METHOD(completionHandler:(NSString *)completionKey)
523
{
524
    [[RNNotificationsBridgeQueue sharedInstance] completeAction:completionKey];
525 526
}

527 528 529 530 531 532 533 534 535 536
RCT_EXPORT_METHOD(abandonPermissions)
{
    [[UIApplication sharedApplication] unregisterForRemoteNotifications];
}

RCT_EXPORT_METHOD(registerPushKit)
{
    [RNNotifications registerPushKit];
}

537 538 539 540 541
RCT_EXPORT_METHOD(setBadgesCount:(int)count)
{
    [[UIApplication sharedApplication] setApplicationIconBadgeNumber:count];
}

542 543 544 545 546 547
RCT_EXPORT_METHOD(backgroundTimeRemaining:(RCTResponseSenderBlock)callback)
{
    NSTimeInterval remainingTime = [UIApplication sharedApplication].backgroundTimeRemaining;
    callback(@[ [NSNumber numberWithDouble:remainingTime] ]);
}

548 549
RCT_EXPORT_METHOD(consumeBackgroundQueue)
{
550 551 552 553
    // Mark JS Thread as ready
    [RNNotificationsBridgeQueue sharedInstance].jsIsReady = YES;

    // Push actions to JS
554 555 556 557 558 559
    [[RNNotificationsBridgeQueue sharedInstance] consumeActionsQueue:^(NSDictionary* action) {
        [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationActionTriggered
                                                            object:self
                                                          userInfo:action];
    }];

560
    // Push background notifications to JS
561
    [[RNNotificationsBridgeQueue sharedInstance] consumeNotificationsQueue:^(NSDictionary* notification) {
562
        [RNNotifications didReceiveRemoteNotification:notification];
563 564
    }];

565 566 567
    // Push opened local notifications
    NSDictionary* openedLocalNotification = [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification;
    if (openedLocalNotification) {
568
        [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil;
569 570 571 572 573 574
        [RNNotifications didNotificationOpen:openedLocalNotification];
    }

    // Push opened remote notifications
    NSDictionary* openedRemoteNotification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification;
    if (openedRemoteNotification) {
575
        [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil;
576 577
        [RNNotifications didNotificationOpen:openedRemoteNotification];
    }
578 579
}

580
RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId)
581
{
582 583 584
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) {
        UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId];
        [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil];
585
    } else {
586 587 588 589 590 591 592 593 594 595
        UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification];
        NSMutableArray* userInfo = localNotification.userInfo.mutableCopy;
        [userInfo setValue:notificationId forKey:@"__id"];
        localNotification.userInfo = userInfo;

        if ([notification objectForKey:@"fireDate"] != nil) {
            [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
        } else {
            [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
        }
596 597 598
    }
}

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

608 609 610
            if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) {
                [[UIApplication sharedApplication] cancelLocalNotification:notification];
            }
611 612 613 614 615 616 617 618 619
        }
    }
}

RCT_EXPORT_METHOD(cancelAllLocalNotifications)
{
    [RCTSharedApplication() cancelAllLocalNotifications];
}

620 621 622
RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
{
    BOOL ans;
623

624 625 626 627 628 629 630 631 632
    if (TARGET_IPHONE_SIMULATOR) {
        ans = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != 0;
    }
    else {
        ans = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
    }
    resolve(@(ans));
}

Ryan Eberhardt's avatar
Ryan Eberhardt committed
633 634 635 636 637 638 639 640 641 642
RCT_EXPORT_METHOD(checkPermissions:(RCTPromiseResolveBlock) resolve
                  reject:(RCTPromiseRejectBlock) reject) {
    UIUserNotificationSettings *currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
    resolve(@{
              @"badge": @((currentSettings.types & UIUserNotificationTypeBadge) > 0),
              @"sound": @((currentSettings.types & UIUserNotificationTypeSound) > 0),
              @"alert": @((currentSettings.types & UIUserNotificationTypeAlert) > 0),
              });
}

643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666
#if !TARGET_OS_TV

RCT_EXPORT_METHOD(removeAllDeliveredNotifications)
{
  if ([UNUserNotificationCenter class]) {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center removeAllDeliveredNotifications];
  }
}

RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray<NSString *> *)identifiers)
{
  if ([UNUserNotificationCenter class]) {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center removeDeliveredNotificationsWithIdentifiers:identifiers];
  }
}

RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback)
{
  if ([UNUserNotificationCenter class]) {
    UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    [center getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
      NSMutableArray<NSDictionary *> *formattedNotifications = [NSMutableArray new];
667

668 669 670 671 672 673 674 675 676 677
      for (UNNotification *notification in notifications) {
        [formattedNotifications addObject:RCTFormatUNNotification(notification)];
      }
      callback(@[formattedNotifications]);
    }];
  }
}

#endif !TARGET_OS_TV

Lidan Hifi's avatar
Lidan Hifi committed
678
@end