RNNotifications.m 10.6 KB
Newer Older
Lidan Hifi's avatar
Lidan Hifi committed
1 2 3 4

#import <UIKit/UIKit.h>
#import "RCTBridge.h"
#import "RCTEventDispatcher.h"
Lidan Hifi's avatar
Lidan Hifi committed
5
#import "RNNotifications.h"
6
#import "RCTConvert.h"
Lidan Hifi's avatar
Lidan Hifi committed
7 8
#import "RCTUtils.h"

Lidan Hifi's avatar
Lidan Hifi committed
9 10
NSString *const RNNotificationCreateAction = @"CREATE";
NSString *const RNNotificationClearAction = @"CLEAR";
Lidan Hifi's avatar
Lidan Hifi committed
11

Lidan Hifi's avatar
Lidan Hifi committed
12 13 14
NSString *const RNNotificationReceivedForeground = @"RNNotificationReceivedForeground";
NSString *const RNNotificationReceivedBackground = @"RNNotificationReceivedBackground";
NSString *const RNNotificationOpened = @"RNNotificationOpened";
Lidan Hifi's avatar
Lidan Hifi committed
15

16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
/*
 * Enum Converters for Interactive Notifications
 */
@implementation RCTConvert (UIUserNotificationActivationMode)
RCT_ENUM_CONVERTER(UIUserNotificationActivationMode, (@{
                                                        @"foreground": @(UIUserNotificationActivationModeForeground),
                                                        @"background": @(UIUserNotificationActivationModeBackground)
                                                        }), UIUserNotificationActivationModeForeground, integerValue)
@end

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

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

Lidan Hifi's avatar
Lidan Hifi committed
41
@implementation RNNotifications
Lidan Hifi's avatar
Lidan Hifi committed
42 43 44 45 46

RCT_EXPORT_MODULE()

@synthesize bridge = _bridge;

47 48 49 50 51 52 53 54 55 56 57
NSMutableDictionary *actionCallbacks;

- (id)init
{
    if (self = [super init]) {
        actionCallbacks = [[NSMutableDictionary alloc] init];
        return self;
    } else {
        return nil;
    }
}
Lidan Hifi's avatar
Lidan Hifi committed
58 59 60 61 62 63 64 65 66

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

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

Lidan Hifi's avatar
Lidan Hifi committed
68 69
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationReceivedForeground:)
Lidan Hifi's avatar
Lidan Hifi committed
70
                                                 name:RNNotificationReceivedForeground
Lidan Hifi's avatar
Lidan Hifi committed
71
                                               object:nil];
Lidan Hifi's avatar
Lidan Hifi committed
72

Lidan Hifi's avatar
Lidan Hifi committed
73 74
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationReceivedBackground:)
Lidan Hifi's avatar
Lidan Hifi committed
75
                                                 name:RNNotificationReceivedBackground
Lidan Hifi's avatar
Lidan Hifi committed
76
                                               object:nil];
Lidan Hifi's avatar
Lidan Hifi committed
77

Lidan Hifi's avatar
Lidan Hifi committed
78 79
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(handleNotificationOpened:)
Lidan Hifi's avatar
Lidan Hifi committed
80
                                                 name:RNNotificationOpened
Lidan Hifi's avatar
Lidan Hifi committed
81 82 83 84
                                               object:nil];
}

/*
85
 * Public Methods
Lidan Hifi's avatar
Lidan Hifi committed
86 87 88 89
 */
+ (void)didReceiveRemoteNotification:(NSDictionary *)notification
{
    UIApplicationState state = [UIApplication sharedApplication].applicationState;
Lidan Hifi's avatar
Lidan Hifi committed
90

Lidan Hifi's avatar
Lidan Hifi committed
91
    if (state == UIApplicationStateActive) {
92
        // Notification received foreground
Lidan Hifi's avatar
Lidan Hifi committed
93 94
        [self didReceiveNotificationOnForegroundState:notification];
    } else if (state == UIApplicationStateInactive) {
95
        // Notification opened
Lidan Hifi's avatar
Lidan Hifi committed
96 97
        [self didNotificationOpen:notification];
    } else {
98
        // Notification received background
Lidan Hifi's avatar
Lidan Hifi committed
99 100 101 102 103 104 105
        [self didReceiveNotificationOnBackgroundState:notification];
    }
}

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

Lidan Hifi's avatar
Lidan Hifi committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120
    if (state == UIApplicationStateInactive) {
        NSString* notificationId = [notification.userInfo objectForKey:@"notificationId"];
        if (notificationId) {
            [self clearNotificationFromNotificationsCenter:notificationId];
        }
        [self didNotificationOpen:notification.userInfo];
    }
}

/*
 * Notification handlers
 */
+ (void)didReceiveNotificationOnForegroundState:(NSDictionary *)notification
{
Lidan Hifi's avatar
Lidan Hifi committed
121
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedForeground
Lidan Hifi's avatar
Lidan Hifi committed
122 123 124 125 126 127
                                                        object:self
                                                      userInfo:notification];
}

+ (void)didReceiveNotificationOnBackgroundState:(NSDictionary *)notification
{
128 129 130 131
    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
132 133 134

    if (action) {
        // create or delete notification
Lidan Hifi's avatar
Lidan Hifi committed
135
        if ([action isEqualToString: RNNotificationCreateAction]
Lidan Hifi's avatar
Lidan Hifi committed
136 137 138
            && notificationId
            && alert) {
            [self dispatchLocalNotificationFromNotification:notification];
Lidan Hifi's avatar
Lidan Hifi committed
139 140

        } else if ([action isEqualToString: RNNotificationClearAction] && notificationId) {
Lidan Hifi's avatar
Lidan Hifi committed
141 142 143
            [self clearNotificationFromNotificationsCenter:notificationId];
        }
    }
Lidan Hifi's avatar
Lidan Hifi committed
144 145

    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationReceivedBackground
Lidan Hifi's avatar
Lidan Hifi committed
146 147 148 149 150 151
                                                        object:self
                                                      userInfo:notification];
}

+ (void)didNotificationOpen:(NSDictionary *)notification
{
Lidan Hifi's avatar
Lidan Hifi committed
152
    [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationOpened
Lidan Hifi's avatar
Lidan Hifi committed
153 154 155 156 157 158 159 160 161 162
                                                        object:self
                                                      userInfo:notification];
}

/*
 * Helper methods
 */

+ (void)dispatchLocalNotificationFromNotification:(NSDictionary *)notification
{
163 164 165 166
    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
167 168

    if ([action isEqualToString: RNNotificationCreateAction]
Lidan Hifi's avatar
Lidan Hifi committed
169 170
        && notificationId
        && alert) {
Lidan Hifi's avatar
Lidan Hifi committed
171

Lidan Hifi's avatar
Lidan Hifi committed
172 173 174 175
        // trigger new client push notification
        UILocalNotification* note = [[UILocalNotification alloc] init];
        note.alertTitle = [alert objectForKey:@"title"];
        note.alertBody = [alert objectForKey:@"body"];
176
        note.userInfo = notification;
177
        note.soundName = [managedAps objectForKey:@"sound"];
178
        note.category = [managedAps objectForKey:@"category"];
Lidan Hifi's avatar
Lidan Hifi committed
179

Lidan Hifi's avatar
Lidan Hifi committed
180
        [[UIApplication sharedApplication] presentLocalNotificationNow:note];
Lidan Hifi's avatar
Lidan Hifi committed
181

Lidan Hifi's avatar
Lidan Hifi committed
182 183 184 185 186
        // 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
187

Lidan Hifi's avatar
Lidan Hifi committed
188 189 190 191 192 193 194 195 196 197
        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
198

Lidan Hifi's avatar
Lidan Hifi committed
199 200 201
        // delete the notification
        [[UIApplication sharedApplication] cancelLocalNotification:notification];
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:notificationKey];
Lidan Hifi's avatar
Lidan Hifi committed
202

203 204
        NSLog(@"Local notification removed: %@", notificationKey);

Lidan Hifi's avatar
Lidan Hifi committed
205 206 207 208 209 210 211 212 213
        return;
    }
}

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

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
+ (UIMutableUserNotificationAction *)parseAction:(NSDictionary *)json
{
    UIMutableUserNotificationAction* action =[[UIMutableUserNotificationAction alloc] init];
    action.activationMode = [RCTConvert UIUserNotificationActivationMode:json[@"activationMode"]];
    action.behavior = [RCTConvert UIUserNotificationActionBehavior:json[@"behavior"]];
    action.authenticationRequired = [RCTConvert BOOL:json[@"authenticationRequired"]];
    action.destructive = [RCTConvert BOOL:json[@"destructive"]];
    action.title = json[@"title"];
    action.identifier = json[@"identifier"];

    return action;
}

+ (UIMutableUserNotificationCategory *)parseCategory:(NSDictionary *)json
{
    UIMutableUserNotificationCategory* category = [[UIMutableUserNotificationCategory alloc] init];
    category.identifier = json[@"identifier"];

    // category actions
    NSMutableArray* actions = [[NSMutableArray alloc] init];
    for (NSDictionary* actionJson in [RCTConvert NSArray:json[@"actions"]]) {
        [actions addObject:[self parseAction:actionJson]];
    }

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

    return category;
}

+ (void)updateNotificationCategories:(NSArray *)json
{
    NSMutableSet* categories = [[NSMutableSet alloc] init];
    for (NSDictionary* categoryJson in json) {
        [categories addObject:[self parseCategory:categoryJson]];
    }

    UIUserNotificationType types = (UIUserNotificationType) (UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert);
    UIUserNotificationSettings* settings = [UIUserNotificationSettings settingsForTypes:types categories:categories];

    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}

Lidan Hifi's avatar
Lidan Hifi committed
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
- (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];
}

/*
 * React Native exported methods
 */
274
RCT_EXPORT_METHOD(updateNotificationCategories:(NSArray *)json)
Lidan Hifi's avatar
Lidan Hifi committed
275
{
276
    [RNNotifications updateNotificationCategories:json];
Lidan Hifi's avatar
Lidan Hifi committed
277 278 279
}

@end