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

#import <UIKit/UIKit.h>
3
#import <PushKit/PushKit.h>
4 5 6 7 8
#import <React/RCTBridge.h>
#import <React/RCTEventDispatcher.h>
#import "RNNotifications.h"
#import <React/RCTConvert.h>
#import <React/RCTUtils.h>
9
#import "RNNotificationsBridgeQueue.h"
10
#import <UserNotifications/UserNotifications.h>
yogevbd's avatar
WIP  
yogevbd committed
11
#import "RNEventEmitter.h"
12

13 14
NSString* const RNNotificationCreateAction = @"CREATE";
NSString* const RNNotificationClearAction = @"CLEAR";
Lidan Hifi's avatar
Lidan Hifi committed
15

16
@implementation RNNotifications
17

18 19
RCT_EXPORT_MODULE()

20 21
@synthesize bridge = _bridge;

yogevbd's avatar
WIP  
yogevbd committed
22 23 24 25 26 27 28 29 30 31
+ (instancetype)sharedInstance {
    static RNNotifications *sharedInstance = nil;
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        sharedInstance = [[RNNotifications alloc] init];
    });
    return sharedInstance;
}

32
- (void)dealloc
Lidan Hifi's avatar
Lidan Hifi committed
33
{
34
    [[NSNotificationCenter defaultCenter] removeObserver:self];
Lidan Hifi's avatar
Lidan Hifi committed
35 36
}

Artal Druk's avatar
Artal Druk committed
37 38 39
+ (BOOL)requiresMainQueueSetup {
    return YES;
}
40

Lidan Hifi's avatar
Lidan Hifi committed
41 42
- (void)setBridge:(RCTBridge *)bridge
{
43 44 45 46
    _bridge = bridge;
    [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    UILocalNotification *localNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
    [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = localNotification ? localNotification.userInfo : nil;
47
}
48

49 50 51
/*
 * Public Methods
 */
52

yogevbd's avatar
WIP  
yogevbd committed
53
- (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken {
54
    NSString *tokenRepresentation = [deviceToken isKindOfClass:[NSString class]] ? deviceToken : [self deviceTokenToString:deviceToken];
yogevbd's avatar
WIP  
yogevbd committed
55
    [RNEventEmitter sendEvent:Registered body:@{@"deviceToken": tokenRepresentation}];
56
}
Lidan Hifi's avatar
Lidan Hifi committed
57

yogevbd's avatar
WIP  
yogevbd committed
58 59
- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
    [RNEventEmitter sendEvent:RegistrationFailed body:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}];
60
}
Lidan Hifi's avatar
Lidan Hifi committed
61

yogevbd's avatar
WIP  
yogevbd committed
62
- (void)didReceiveForegroundNotification:(UNNotification *)notification {
63
    if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) {
yogevbd's avatar
WIP  
yogevbd committed
64
        [self didReceiveNotificationOnForegroundState:notification];
65 66
    }
}
67

yogevbd's avatar
WIP  
yogevbd committed
68 69 70
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response {
    if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES && [response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) {
        [self didNotificationOpen:response.notification.request.content.userInfo];
71
    }
72 73
}

yogevbd's avatar
WIP  
yogevbd committed
74
- (void)handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
75
{
76
    [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:notification.userInfo completionHandler:completionHandler];
77 78
}

yogevbd's avatar
WIP  
yogevbd committed
79
- (void)handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
Lidan Hifi's avatar
Lidan Hifi committed
80
{
81
    [self emitNotificationActionForIdentifier:identifier responseInfo:responseInfo userInfo:userInfo completionHandler:completionHandler];
82 83
}

yogevbd's avatar
WIP  
yogevbd committed
84 85 86 87 88 89
- (void)setBadgeForNotification:(NSDictionary *)notification {
    if ([[notification objectForKey:@"aps"] objectForKey:@"badge"]){
        [[UIApplication sharedApplication] setApplicationIconBadgeNumber:[[[notification objectForKey:@"aps"] objectForKey:@"badge"] intValue]];
    }
}

Lidan Hifi's avatar
Lidan Hifi committed
90 91 92
/*
 * Notification handlers
 */
yogevbd's avatar
WIP  
yogevbd committed
93 94
- (void)didReceiveNotificationOnForegroundState:(UNNotification *)notification {
    [RNEventEmitter sendEvent:NotificationReceivedForeground body:notification.request.content.userInfo];
95
}
Lidan Hifi's avatar
Lidan Hifi committed
96

yogevbd's avatar
WIP  
yogevbd committed
97
- (void)didReceiveNotificationOnBackgroundState:(NSDictionary *)notification {
98 99 100 101
    NSDictionary* managedAps  = [notification objectForKey:@"managedAps"];
    NSDictionary* alert = [managedAps objectForKey:@"alert"];
    NSString* action = [managedAps objectForKey:@"action"];
    NSString* notificationId = [managedAps objectForKey:@"notificationId"];
102

Lidan Hifi's avatar
Lidan Hifi committed
103 104
    if (action) {
        // create or delete notification
Lidan Hifi's avatar
Lidan Hifi committed
105
        if ([action isEqualToString: RNNotificationCreateAction]
Lidan Hifi's avatar
Lidan Hifi committed
106 107 108
            && notificationId
            && alert) {
            [self dispatchLocalNotificationFromNotification:notification];
109

Lidan Hifi's avatar
Lidan Hifi committed
110
        } else if ([action isEqualToString: RNNotificationClearAction] && notificationId) {
Lidan Hifi's avatar
Lidan Hifi committed
111 112 113
            [self clearNotificationFromNotificationsCenter:notificationId];
        }
    }
114

yogevbd's avatar
WIP  
yogevbd committed
115
    [RNEventEmitter sendEvent:NotificationReceivedBackground body:notification];
Lidan Hifi's avatar
Lidan Hifi committed
116 117
}

yogevbd's avatar
WIP  
yogevbd committed
118 119
- (void)didNotificationOpen:(NSDictionary *)notification {
    [RNEventEmitter sendEvent:NotificationOpened body:notification];
Lidan Hifi's avatar
Lidan Hifi committed
120 121 122 123 124
}

/*
 * Helper methods
 */
yogevbd's avatar
WIP  
yogevbd committed
125
- (void)dispatchLocalNotificationFromNotification:(NSDictionary *)notification {
126 127 128 129
    NSDictionary* managedAps  = [notification objectForKey:@"managedAps"];
    NSDictionary* alert = [managedAps objectForKey:@"alert"];
    NSString* action = [managedAps objectForKey:@"action"];
    NSString* notificationId = [managedAps objectForKey:@"notificationId"];
130

Lidan Hifi's avatar
Lidan Hifi committed
131
    if ([action isEqualToString: RNNotificationCreateAction]
Lidan Hifi's avatar
Lidan Hifi committed
132 133
        && notificationId
        && alert) {
134

Lidan Hifi's avatar
Lidan Hifi committed
135
        // trigger new client push notification
136
        UILocalNotification* note = [UILocalNotification new];
Lidan Hifi's avatar
Lidan Hifi committed
137 138
        note.alertTitle = [alert objectForKey:@"title"];
        note.alertBody = [alert objectForKey:@"body"];
139
        note.userInfo = notification;
140
        note.soundName = [managedAps objectForKey:@"sound"];
141
        note.category = [managedAps objectForKey:@"category"];
142

Lidan Hifi's avatar
Lidan Hifi committed
143
        [[UIApplication sharedApplication] presentLocalNotificationNow:note];
144

Lidan Hifi's avatar
Lidan Hifi committed
145 146 147 148 149
        // 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];
150

Lidan Hifi's avatar
Lidan Hifi committed
151 152 153 154
        NSLog(@"Local notification was triggered: %@", notificationKey);
    }
}

yogevbd's avatar
WIP  
yogevbd committed
155
- (void)clearNotificationFromNotificationsCenter:(NSString *)notificationId {
Lidan Hifi's avatar
Lidan Hifi committed
156 157 158 159
    NSString* notificationKey = [self buildNotificationKeyfromNotification:notificationId];
    NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:notificationKey];
    if (data) {
        UILocalNotification* notification = [NSKeyedUnarchiver unarchiveObjectWithData: data];
160

Lidan Hifi's avatar
Lidan Hifi committed
161 162 163
        // delete the notification
        [[UIApplication sharedApplication] cancelLocalNotification:notification];
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:notificationKey];
164

165
        NSLog(@"Local notification removed: %@", notificationKey);
166

Lidan Hifi's avatar
Lidan Hifi committed
167 168 169 170
        return;
    }
}

yogevbd's avatar
WIP  
yogevbd committed
171
- (NSString *)buildNotificationKeyfromNotification:(NSString *)notificationId {
Lidan Hifi's avatar
Lidan Hifi committed
172 173 174
    return [NSString stringWithFormat:@"%@.%@", [[NSBundle mainBundle] bundleIdentifier], notificationId];
}

yogevbd's avatar
WIP  
yogevbd committed
175
- (NSString *)deviceTokenToString:(NSData *)deviceToken {
176 177 178 179 180 181
    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]];
    }
182

183 184 185
    return [result copy];
}

yogevbd's avatar
WIP  
yogevbd committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
- (void)requestPermissionsWithCategories:(NSMutableSet *)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) {
                        [[UIApplication sharedApplication] registerForRemoteNotifications];
                    }
                }];
            } else {
                
            }
        }
    }];
203 204
}

yogevbd's avatar
WIP  
yogevbd committed
205
- (void)emitNotificationActionForIdentifier:(NSString *)identifier responseInfo:(NSDictionary *)responseInfo userInfo:(NSDictionary *)userInfo  completionHandler:(void (^)())completionHandler {
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
    NSString* completionKey = [NSString stringWithFormat:@"%@.%@", identifier, [NSString stringWithFormat:@"%d", (long)[[NSDate date] timeIntervalSince1970]]];
    NSMutableDictionary* info = [[NSMutableDictionary alloc] initWithDictionary:@{ @"identifier": identifier, @"completionKey": completionKey }];

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

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

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

    if ([RNNotificationsBridgeQueue sharedInstance].jsIsReady == YES) {
yogevbd's avatar
WIP  
yogevbd committed
224
        [RNEventEmitter sendEvent:NotificationActionReceived body:userInfo];
225 226 227
    }
}

yogevbd's avatar
WIP  
yogevbd committed
228
- (void)registerPushKit {
229 230 231 232 233 234 235 236
    // 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];
}

yogevbd's avatar
WIP  
yogevbd committed
237 238
- (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type {
    [RNEventEmitter sendEvent:PushKitRegistered body:@{@"pushKitToken": [self deviceTokenToString:credentials.token]}];
239
}
240

yogevbd's avatar
WIP  
yogevbd committed
241 242
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type {
//    [RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload];
243 244
}

Lidan Hifi's avatar
Lidan Hifi committed
245
@end