From 89a1e94a155ddf6290646e9ecb038db01941a10f Mon Sep 17 00:00:00 2001 From: Andrew Imm Date: Wed, 3 Jun 2015 14:11:20 -0700 Subject: [PATCH] Add an event for remote notification registration, and improve permissions request Summary: In order to add Push support to the Parse JS SDK in React Native, we need a way to receive the APNS device token from the JS context. This adds another event to PushNotificationIOS, so that code can respond to a successful registration. Additionally, I've updated the `requestPermissions` call to accept an optional map of parameters. This way, developers can request a subset of user notification types. Closes https://github.com/facebook/react-native/pull/1304 Github Author: Andrew Imm Test Plan: Imported from GitHub, without a `Test Plan:` line. --- .../PushNotificationIOS.js | 77 +++++++++++++++---- .../RCTPushNotificationManager.h | 1 + .../RCTPushNotificationManager.m | 77 ++++++++++++++----- 3 files changed, 117 insertions(+), 38 deletions(-) diff --git a/Libraries/PushNotificationIOS/PushNotificationIOS.js b/Libraries/PushNotificationIOS/PushNotificationIOS.js index 4d03c6641..732d4c029 100644 --- a/Libraries/PushNotificationIOS/PushNotificationIOS.js +++ b/Libraries/PushNotificationIOS/PushNotificationIOS.js @@ -20,6 +20,7 @@ var _initialNotification = RCTPushNotificationManager && RCTPushNotificationManager.initialNotification; var DEVICE_NOTIF_EVENT = 'remoteNotificationReceived'; +var NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered'; /** * Handle push notifications for your app, including permission handling and @@ -49,30 +50,72 @@ class PushNotificationIOS { } /** - * Attaches a listener to remote notifications while the app is running in the - * foreground or the background. + * Attaches a listener to remote notification events while the app is running + * in the foreground or the background. * - * The handler will get be invoked with an instance of `PushNotificationIOS` + * Valid events are: + * + * - `notification` : Fired when a remote notification is received. The + * handler will be invoked with an instance of `PushNotificationIOS`. + * - `register`: Fired when the user registers for remote notifications. The + * handler will be invoked with a hex string representing the deviceToken. */ static addEventListener(type: string, handler: Function) { invariant( - type === 'notification', - 'PushNotificationIOS only supports `notification` events' - ); - _notifHandlers[handler] = RCTDeviceEventEmitter.addListener( - DEVICE_NOTIF_EVENT, - (notifData) => { - handler(new PushNotificationIOS(notifData)); - } + type === 'notification' || type === 'register', + 'PushNotificationIOS only supports `notification` and `register` events' ); + if (type === 'notification') { + _notifHandlers[handler] = RCTDeviceEventEmitter.addListener( + DEVICE_NOTIF_EVENT, + (notifData) => { + handler(new PushNotificationIOS(notifData)); + } + ); + } else if (type === 'register') { + _notifHandlers[handler] = RCTDeviceEventEmitter.addListener( + NOTIF_REGISTER_EVENT, + (registrationInfo) => { + handler(registrationInfo.deviceToken); + } + ); + } } /** - * Requests all notification permissions from iOS, prompting the user's - * dialog box. + * Requests notification permissions from iOS, prompting the user's + * dialog box. By default, it will request all notification permissions, but + * a subset of these can be requested by passing a map of requested + * permissions. + * The following permissions are supported: + * + * - `alert` + * - `badge` + * - `sound` + * + * If a map is provided to the method, only the permissions with truthy values + * will be requested. */ - static requestPermissions() { - RCTPushNotificationManager.requestPermissions(); + static requestPermissions(permissions?: { + alert?: boolean, + badge?: boolean, + sound?: boolean + }) { + var requestedPermissions = {}; + if (permissions) { + requestedPermissions = { + alert: !!permissions.alert, + badge: !!permissions.badge, + sound: !!permissions.sound + }; + } else { + requestedPermissions = { + alert: true, + badge: true, + sound: true + }; + } + RCTPushNotificationManager.requestPermissions(requestedPermissions); } /** @@ -97,8 +140,8 @@ class PushNotificationIOS { */ static removeEventListener(type: string, handler: Function) { invariant( - type === 'notification', - 'PushNotificationIOS only supports `notification` events' + type === 'notification' || type === 'register', + 'PushNotificationIOS only supports `notification` and `register` events' ); if (!_notifHandlers[handler]) { return; diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h index ef1ba1496..194bbc5dd 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.h +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.h @@ -14,6 +14,7 @@ @interface RCTPushNotificationManager : NSObject + (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; ++ (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; + (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification; @end diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m index 4846c885e..1966c6045 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.m +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.m @@ -12,7 +12,18 @@ #import "RCTBridge.h" #import "RCTEventDispatcher.h" +#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 + +#define UIUserNotificationTypeAlert UIRemoteNotificationTypeAlert +#define UIUserNotificationTypeBadge UIRemoteNotificationTypeBadge +#define UIUserNotificationTypeSound UIRemoteNotificationTypeSound +#define UIUserNotificationTypeNone UIRemoteNotificationTypeNone +#define UIUserNotificationType UIRemoteNotificationType + +#endif + NSString *const RCTRemoteNotificationReceived = @"RemoteNotificationReceived"; +NSString *const RCTRemoteNotificationsRegistered = @"RemoteNotificationsRegistered"; @implementation RCTPushNotificationManager { @@ -30,6 +41,10 @@ RCT_EXPORT_MODULE() selector:@selector(handleRemoteNotificationReceived:) name:RCTRemoteNotificationReceived object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(handleRemoteNotificationsRegistered:) + name:RCTRemoteNotificationsRegistered + object:nil]; } return self; } @@ -52,6 +67,21 @@ RCT_EXPORT_MODULE() } } ++ (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken +{ + NSMutableString *hexString = [NSMutableString string]; + const unsigned char *bytes = [deviceToken bytes]; + for (int i = 0; i < [deviceToken length]; i++) { + [hexString appendFormat:@"%02x", bytes[i]]; + } + NSDictionary *userInfo = @{ + @"deviceToken" : [hexString copy] + }; + [[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationsRegistered + object:self + userInfo:userInfo]; +} + + (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification { [[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteNotificationReceived @@ -65,6 +95,12 @@ RCT_EXPORT_MODULE() body:[notification userInfo]]; } +- (void)handleRemoteNotificationsRegistered:(NSNotification *)notification +{ + [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistered" + body:[notification userInfo]]; +} + /** * Update the application icon badge number on the home screen */ @@ -83,36 +119,35 @@ RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback ]); } -RCT_EXPORT_METHOD(requestPermissions) +RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions) { - Class _UIUserNotificationSettings; - if ((_UIUserNotificationSettings = NSClassFromString(@"UIUserNotificationSettings"))) { - UIUserNotificationType types = UIUserNotificationTypeSound | UIUserNotificationTypeBadge | UIUserNotificationTypeAlert; - UIUserNotificationSettings *notificationSettings = [_UIUserNotificationSettings settingsForTypes:types categories:nil]; - [[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings]; + UIUserNotificationType types = UIRemoteNotificationTypeNone; + if (permissions) { + if ([permissions[@"alert"] boolValue]) { + types |= UIUserNotificationTypeAlert; + } + if ([permissions[@"badge"] boolValue]) { + types |= UIUserNotificationTypeBadge; + } + if ([permissions[@"sound"] boolValue]) { + types |= UIUserNotificationTypeSound; + } } else { + types = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound; + } -#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 - - [[UIApplication sharedApplication] registerForRemoteNotificationTypes: - UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert]; - +#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0 + id notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil]; + [[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings]; + [[UIApplication sharedApplication] registerForRemoteNotifications]; +#else + [[UIApplication sharedApplication] registerForRemoteNotificationTypes:types]; #endif - } } RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback) { - -#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0 - -#define UIUserNotificationTypeAlert UIRemoteNotificationTypeAlert -#define UIUserNotificationTypeBadge UIRemoteNotificationTypeBadge -#define UIUserNotificationTypeSound UIRemoteNotificationTypeSound - -#endif - NSUInteger types = 0; if ([UIApplication instancesRespondToSelector:@selector(currentUserNotificationSettings)]) { types = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];