Skip to content

Commit

Permalink
Resolve and reject promise for PushNotificationIOS.requestPermissions
Browse files Browse the repository at this point in the history
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
  • Loading branch information
p-sun authored and facebook-github-bot committed Feb 7, 2020
1 parent a35efb9 commit edfdafc
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 71 deletions.
83 changes: 30 additions & 53 deletions Libraries/PushNotificationIOS/RCTPushNotificationManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -83,9 +82,6 @@ @interface RCTPushNotificationManager () <NativePushNotificationManagerIOS>
#endif //TARGET_OS_TV / TARGET_OS_UIKITFORMAC

@implementation RCTPushNotificationManager
{
RCTPromiseResolveBlock _requestPermissionsResolveBlock;
}

#if !TARGET_OS_TV && !TARGET_OS_UIKITFORMAC

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;

Expand All @@ -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)
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ class NotificationExample extends React.Component<{...}> {
'localNotification',
this._onLocalNotification,
);

PushNotificationIOS.requestPermissions();
}

componentWillUnmount() {
Expand Down Expand Up @@ -173,18 +171,51 @@ class NotificationPermissionExample extends React.Component<
return (
<View>
<Button
onPress={this._showPermissions.bind(this)}
label="Show enabled permissions"
onPress={this._requestPermissions}
label="Request Notifications (Should Display Alert)"
/>
<Text>{JSON.stringify(this.state.permissions)}</Text>
<Button onPress={this._checkPermissions} label="Check permissions" />
<Text style={{textAlign: 'center'}}>
{JSON.stringify(this.state.permissions)}
</Text>
</View>
);
}

_showPermissions() {
_requestPermissions = () => {
PushNotificationIOS.requestPermissions().then(
onFulfill => {
this._showAlert(
'Successfully requested permissions -- ' +
'Alert: ' +
onFulfill.alert.toString() +
', Badge: ' +
onFulfill.badge.toString() +
', Sound: ' +
onFulfill.sound.toString(),
);
this._checkPermissions();
},
(onReject?) => {
this._showAlert('Error requesting permissions');
this._checkPermissions();
},
);
};

_checkPermissions = () => {
PushNotificationIOS.checkPermissions(permissions => {
this.setState({permissions});
});
};

_showAlert(text) {
Alert.alert('Notification Permission', text, [
{
text: 'Dismiss',
onPress: null,
},
]);
}
}

Expand All @@ -202,6 +233,18 @@ const styles = StyleSheet.create({
exports.title = 'PushNotificationIOS';
exports.description = 'Apple PushNotification and badge value';
exports.examples = [
{
title: 'Notifications Permissions',
render(): React.Element<any> {
return <NotificationPermissionExample />;
},
},
{
title: 'Push Notifications',
render(): React.Element<any> {
return <NotificationExample />;
},
},
{
title: 'Badge Number',
render(): React.Element<any> {
Expand All @@ -221,16 +264,4 @@ exports.examples = [
);
},
},
{
title: 'Push Notifications',
render(): React.Element<any> {
return <NotificationExample />;
},
},
{
title: 'Notifications Permissions',
render(): React.Element<any> {
return <NotificationPermissionExample />;
},
},
];

0 comments on commit edfdafc

Please sign in to comment.