From edfdafc7a14e88a2660b95cb220c62f29b1b28c0 Mon Sep 17 00:00:00 2001 From: Paige Sun Date: Fri, 7 Feb 2020 08:48:15 -0800 Subject: [PATCH] Resolve and reject promise for PushNotificationIOS.requestPermissions Summary: **Resolve/Reject Promise** * Add onFulfill and onReject to the `PushNotificationIOS.requestPermissions()` Promise **Replace Apple-deprecated notification method** * Old: In iOS 10, `UIApplication.registerUserNotificationSettings` was deprecated. Calling this would then call the AppDelegate's lifecycle function `didRegisterUserNotificationSettings`, and then in the AppDelegate, we'd call `RCTPushNotificationManager.didRegisterUserNotificationSettings` to return the user settings. [registerusernotificationsettings Doc](https://developer.apple.com/documentation/uikit/uiapplication/1622932-registerusernotificationsettings?language=objc) * New: Replace deprecated function with Apple's recommended `UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler`, which no longer needs the AppDelegate lifecycle method because it directly returns the user's settings in a completion hander. [requestauthorizationwithoptions Doc](https://developer.apple.com/documentation/usernotifications/unusernotificationcenter/1649527-requestauthorizationwithoptions?language=objc) **Add Tests** * Add tests on `PushNotificationIOSExample.js` to test that the onFulfill and onReject are called * On `PushNotificationIOSExample.js`, instead of asking permission upon page load, ask for permission when the user taps the button "Request Notifications (Should Display Alert)". * Before, asking for permission multiple times before would result in the RN error "cannot call requestPermissions twice before the first has returned", now you can ask for permission as many times as you want because I've removed the now unused `RCTPromiseResolveBlock`. **Future** If this works on device (we have to land this to test push on device), we can delete `RTCPushNotificationManager.didRegisterUserNotificationSettings` which is being called from several apps. Changelog: [iOS] [Added] Resolve and reject promise for PushNotificationIOS.requestPermissions Reviewed By: PeteTheHeat Differential Revision: D19700061 fbshipit-source-id: 02ba815787efc9047f33ffcdfafe962b134afe6d --- .../RCTPushNotificationManager.mm | 83 +++++++------------ .../PushNotificationIOSExample.js | 67 +++++++++++---- 2 files changed, 79 insertions(+), 71 deletions(-) diff --git a/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm b/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm index b2ba94d497bef0..f01e5013e2c6fb 100644 --- a/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm +++ b/Libraries/PushNotificationIOS/RCTPushNotificationManager.mm @@ -21,7 +21,6 @@ static NSString *const kLocalNotificationReceived = @"LocalNotificationReceived"; static NSString *const kRemoteNotificationsRegistered = @"RemoteNotificationsRegistered"; -static NSString *const kRegisterUserNotificationSettings = @"RegisterUserNotificationSettings"; static NSString *const kRemoteNotificationRegistrationFailed = @"RemoteNotificationRegistrationFailed"; static NSString *const kErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS"; @@ -83,9 +82,6 @@ @interface RCTPushNotificationManager () #endif //TARGET_OS_TV / TARGET_OS_UIKITFORMAC @implementation RCTPushNotificationManager -{ - RCTPromiseResolveBlock _requestPermissionsResolveBlock; -} #if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC @@ -152,10 +148,6 @@ - (void)startObserving selector:@selector(handleRemoteNotificationReceived:) name:RCTRemoteNotificationReceived object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(handleRegisterUserNotificationSettings:) - name:kRegisterUserNotificationSettings - object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRemoteNotificationsRegistered:) name:kRemoteNotificationsRegistered @@ -181,12 +173,6 @@ - (void)stopObserving + (void)didRegisterUserNotificationSettings:(__unused UIUserNotificationSettings *)notificationSettings { - if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) { - [RCTSharedApplication() registerForRemoteNotifications]; - [[NSNotificationCenter defaultCenter] postNotificationName:kRegisterUserNotificationSettings - object:self - userInfo:@{@"notificationSettings": notificationSettings}]; - } } + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken @@ -272,25 +258,6 @@ - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification [self sendEventWithName:@"remoteNotificationRegistrationError" body:errorDetails]; } -- (void)handleRegisterUserNotificationSettings:(NSNotification *)notification -{ - if (_requestPermissionsResolveBlock == nil) { - return; - } - - UIUserNotificationSettings *notificationSettings = notification.userInfo[@"notificationSettings"]; - NSDictionary *notificationTypes = @{ - @"alert": @((notificationSettings.types & UIUserNotificationTypeAlert) > 0), - @"sound": @((notificationSettings.types & UIUserNotificationTypeSound) > 0), - @"badge": @((notificationSettings.types & UIUserNotificationTypeBadge) > 0), - }; - - _requestPermissionsResolveBlock(notificationTypes); - // Clean up listener added in requestPermissions - [self removeListeners:1]; - _requestPermissionsResolveBlock = nil; -} - RCT_EXPORT_METHOD(onFinishRemoteNotification:(NSString *)notificationId fetchResult:(NSString *)fetchResult) { UIBackgroundFetchResult result = [RCTConvert UIBackgroundFetchResult:fetchResult]; RCTRemoteNotificationCallback completionHandler = self.remoteNotificationCallbacks[notificationId]; @@ -322,19 +289,13 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { - if (RCTRunningInAppExtension()) { - reject(kErrorUnableToRequestPermissions, nil, RCTErrorWithMessage(@"Requesting push notifications is currently unavailable in an app extension")); - return; - } - - if (_requestPermissionsResolveBlock != nil) { - RCTLogError(@"Cannot call requestPermissions twice before the first has returned."); - return; - } + if (RCTRunningInAppExtension()) { + reject(kErrorUnableToRequestPermissions, nil, RCTErrorWithMessage(@"Requesting push notifications is currently unavailable in an app extension")); + return; + } // Add a listener to make sure that startObserving has been called [self addListener:@"remoteNotificationsRegistered"]; - _requestPermissionsResolveBlock = resolve; UIUserNotificationType types = UIUserNotificationTypeNone; @@ -348,9 +309,18 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification types |= UIUserNotificationTypeSound; } - UIUserNotificationSettings *notificationSettings = - [UIUserNotificationSettings settingsForTypes:types categories:nil]; - [RCTSharedApplication() registerUserNotificationSettings:notificationSettings]; + [UNUserNotificationCenter.currentNotificationCenter + requestAuthorizationWithOptions:types + completionHandler:^(BOOL granted, NSError *_Nullable error) { + if (error != NULL) { + reject(@"-1", @"Error - Push authorization request failed.", error); + } else { + [RCTSharedApplication() registerForRemoteNotifications]; + [UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { + resolve(RCTPromiseResolveValueForUNNotificationSettings(settings)); + }]; + } + }]; } RCT_EXPORT_METHOD(abandonPermissions) @@ -361,16 +331,23 @@ - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback) { if (RCTRunningInAppExtension()) { - callback(@[@{@"alert": @NO, @"badge": @NO, @"sound": @NO}]); + callback(@[RCTSettingsDictForUNNotificationSettings(NO, NO, NO)]); return; } - NSUInteger types = [RCTSharedApplication() currentUserNotificationSettings].types; - callback(@[@{ - @"alert": @((types & UIUserNotificationTypeAlert) > 0), - @"badge": @((types & UIUserNotificationTypeBadge) > 0), - @"sound": @((types & UIUserNotificationTypeSound) > 0), - }]); + [UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) { + callback(@[RCTPromiseResolveValueForUNNotificationSettings(settings)]); + }]; +} + +static inline NSDictionary *RCTPromiseResolveValueForUNNotificationSettings(UNNotificationSettings* _Nonnull settings) { + return RCTSettingsDictForUNNotificationSettings(settings.alertSetting == UNNotificationSettingEnabled, + settings.badgeSetting == UNNotificationSettingEnabled, + settings.soundSetting == UNNotificationSettingEnabled); +} + +static inline NSDictionary *RCTSettingsDictForUNNotificationSettings(BOOL alert, BOOL badge, BOOL sound) { + return @{@"alert": @(alert), @"badge": @(badge), @"sound": @(sound)}; } RCT_EXPORT_METHOD(presentLocalNotification:(JS::NativePushNotificationManagerIOS::Notification &)notification) diff --git a/RNTester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js b/RNTester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js index c291a7a6e5ef07..93127afb755999 100644 --- a/RNTester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js +++ b/RNTester/js/examples/PushNotificationIOS/PushNotificationIOSExample.js @@ -49,8 +49,6 @@ class NotificationExample extends React.Component<{...}> { 'localNotification', this._onLocalNotification, ); - - PushNotificationIOS.requestPermissions(); } componentWillUnmount() { @@ -173,18 +171,51 @@ class NotificationPermissionExample extends React.Component< return (