-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Repeated requests for iOS Notifications with other options are not possible #20072
Comments
… options expo#20072 On iOS, the permissions for Notification can be requested with a set of [options](https://developer.apple.com/documentation/usernotifications/unauthorizationoptions). The most common ones are to display alerts or play sounds, but they can also enable notifications on CarPlay displays, or enable critical alerts. Over the course of its lifetime, an app may introduce aditional capabilities, such as critical alerts or other, newly introduced iOS features and thus, must call `requestAuthorization(options:completionHandler:)` again with a new set of options. Expo Notifications supports all these [options](https://docs.expo.dev/versions/latest/sdk/notifications/#requestpermissionsasyncrequest-notificationpermissionsrequest-promisenotificationpermissionsstatus) for `requestPermissionsAsync`. The issue is, that after an app is newly installed on a device, the first call to `Notification.requestPermissionsAsync` will prompt the user, and set the given options. **However, subsequent calls will have no effect.** This means, for the user to take advantage of new features, they will have to de-install and re-install the app, which is completely unacceptable. The root of the problem can be found in [EXPermissionsService.m:111](https://github.com/expo/expo/blob/168ee43f71f005baa11edf98e518593443e1807a/packages/expo-modules-core/ios/Services/Permissions/EXPermissionsService.m#L111): ```objc BOOL isGranted = [EXPermissionsService statusForPermission:permission] == EXPermissionStatusGranted; permission[@"granted"] = @(isGranted); if (isGranted) { return onResult(permission); } [self askForGlobalPermissionUsingRequesterClass:requesterClass withResolver:onResult withRejecter:reject]; ``` This means that permissions are only ever requested again, if permission has not previously been granted. This however **does not consider different options**, but only if any sort of notification permissions have been granted. This means that if previously only Alerts have been requested, a new request for Alerts and Sounds will be ignored. On iOS, it is perfectly acceptable to call `requestAuthorization(options:completionHandler:)` repeatedly: > subsequent calls to this method (even with different options do not prompt the user again. It is best practice on iOS to call this method liberally (e.g. at every startup), and the performance hit should not be different from getting the permission status. Also not that several tutorials suggest to check the permission status first, and then requesting it. This is — at least for iOS — not advisable. Therefore the method `askForPermissionUsingRequesterClass` was removed entirely, and instead the method `askForGlobalPermissionUsingRequesterClass` was renamed appropriately.
… options expo#20072 On iOS, the permissions for Notification can be requested with a set of [options](https://developer.apple.com/documentation/usernotifications/unauthorizationoptions). The most common ones are to display alerts or play sounds, but they can also enable notifications on CarPlay displays, or enable critical alerts. Over the course of its lifetime, an app may introduce aditional capabilities, such as critical alerts or other, newly introduced iOS features and thus, must call `requestAuthorization(options:completionHandler:)` again with a new set of options. Expo Notifications supports all these [options](https://docs.expo.dev/versions/latest/sdk/notifications/#requestpermissionsasyncrequest-notificationpermissionsrequest-promisenotificationpermissionsstatus) for `requestPermissionsAsync`. The issue is, that after an app is newly installed on a device, the first call to `Notification.requestPermissionsAsync` will prompt the user, and set the given options. **However, subsequent calls will have no effect.** This means, for the user to take advantage of new features, they will have to de-install and re-install the app, which is completely unacceptable. The root of the problem can be found in [EXPermissionsService.m:111](https://github.com/expo/expo/blob/168ee43f71f005baa11edf98e518593443e1807a/packages/expo-modules-core/ios/Services/Permissions/EXPermissionsService.m#L111): ```objc BOOL isGranted = [EXPermissionsService statusForPermission:permission] == EXPermissionStatusGranted; permission[@"granted"] = @(isGranted); if (isGranted) { return onResult(permission); } [self askForGlobalPermissionUsingRequesterClass:requesterClass withResolver:onResult withRejecter:reject]; ``` This means that permissions are only ever requested again, if permission has not previously been granted. This however **does not consider different options**, but only if any sort of notification permissions have been granted. This means that if previously only Alerts have been requested, a new request for Alerts and Sounds will be ignored. On iOS, it is perfectly acceptable to call `requestAuthorization(options:completionHandler:)` repeatedly: > subsequent calls to this method (even with different options do not prompt the user again. It is best practice on iOS to call this method liberally (e.g. at every startup), and the performance hit should not be different from getting the permission status. Also not that several tutorials suggest to check the permission status first, and then requesting it. This is — at least for iOS — not advisable. Therefore the method `askForPermissionUsingRequesterClass` was removed entirely, and instead the method `askForGlobalPermissionUsingRequesterClass` was renamed appropriately. This was tested with my [test app](https://github.com/below/ExpoNotificationIssue).
A pull request for this issue can be found here: #20086 |
… options expo#20072 On iOS, the permissions for Notification can be requested with a set of [options](https://developer.apple.com/documentation/usernotifications/unauthorizationoptions). The most common ones are to display alerts or play sounds, but they can also enable notifications on CarPlay displays, or enable critical alerts. Over the course of its lifetime, an app may introduce aditional capabilities, such as critical alerts or other, newly introduced iOS features and thus, must call `requestAuthorization(options:completionHandler:)` again with a new set of options. Expo Notifications supports all these [options](https://docs.expo.dev/versions/latest/sdk/notifications/#requestpermissionsasyncrequest-notificationpermissionsrequest-promisenotificationpermissionsstatus) for `requestPermissionsAsync`. The issue is, that after an app is newly installed on a device, the first call to `Notification.requestPermissionsAsync` will prompt the user, and set the given options. **However, subsequent calls will have no effect.** This means, for the user to take advantage of new features, they will have to de-install and re-install the app, which is completely unacceptable. The root of the problem can be found in [EXPermissionsService.m:111](https://github.com/expo/expo/blob/168ee43f71f005baa11edf98e518593443e1807a/packages/expo-modules-core/ios/Services/Permissions/EXPermissionsService.m#L111): ```objc BOOL isGranted = [EXPermissionsService statusForPermission:permission] == EXPermissionStatusGranted; permission[@"granted"] = @(isGranted); if (isGranted) { return onResult(permission); } [self askForGlobalPermissionUsingRequesterClass:requesterClass withResolver:onResult withRejecter:reject]; ``` This means that permissions are only ever requested again, if permission has not previously been granted. This however **does not consider different options**, but only if any sort of notification permissions have been granted. This means that if previously only Alerts have been requested, a new request for Alerts and Sounds will be ignored. On iOS, it is perfectly acceptable to call `requestAuthorization(options:completionHandler:)` repeatedly: > subsequent calls to this method (even with different options do not prompt the user again. It is best practice on iOS to call this method liberally (e.g. at every startup), and the performance hit should not be different from getting the permission status. Also not that several tutorials suggest to check the permission status first, and then requesting it. This is — at least for iOS — not advisable. Therefore the method `askForPermissionUsingRequesterClass` was removed entirely, and instead the method `askForGlobalPermissionUsingRequesterClass` was renamed appropriately. This was tested with my [test app](https://github.com/below/ExpoNotificationIssue).
… options expo#20072 On iOS, the permissions for Notification can be requested with a set of [options](https://developer.apple.com/documentation/usernotifications/unauthorizationoptions). The most common ones are to display alerts or play sounds, but they can also enable notifications on CarPlay displays, or enable critical alerts. Over the course of its lifetime, an app may introduce aditional capabilities, such as critical alerts or other, newly introduced iOS features and thus, must call `requestAuthorization(options:completionHandler:)` again with a new set of options. Expo Notifications supports all these [options](https://docs.expo.dev/versions/latest/sdk/notifications/#requestpermissionsasyncrequest-notificationpermissionsrequest-promisenotificationpermissionsstatus) for `requestPermissionsAsync`. The issue is, that after an app is newly installed on a device, the first call to `Notification.requestPermissionsAsync` will prompt the user, and set the given options. **However, subsequent calls will have no effect.** This means, for the user to take advantage of new features, they will have to de-install and re-install the app, which is completely unacceptable. The root of the problem can be found in [EXPermissionsService.m:111](https://github.com/expo/expo/blob/168ee43f71f005baa11edf98e518593443e1807a/packages/expo-modules-core/ios/Services/Permissions/EXPermissionsService.m#L111): ```objc BOOL isGranted = [EXPermissionsService statusForPermission:permission] == EXPermissionStatusGranted; permission[@"granted"] = @(isGranted); if (isGranted) { return onResult(permission); } [self askForGlobalPermissionUsingRequesterClass:requesterClass withResolver:onResult withRejecter:reject]; ``` This means that permissions are only ever requested again, if permission has not previously been granted. This however **does not consider different options**, but only if any sort of notification permissions have been granted. This means that if previously only Alerts have been requested, a new request for Alerts and Sounds will be ignored. On iOS, it is perfectly acceptable to call `requestAuthorization(options:completionHandler:)` repeatedly: > subsequent calls to this method (even with different options do not prompt the user again. It is best practice on iOS to call this method liberally (e.g. at every startup), and the performance hit should not be different from getting the permission status. Also not that several tutorials suggest to check the permission status first, and then requesting it. This is — at least for iOS — not advisable. Therefore the method `askForPermissionUsingRequesterClass` was removed entirely, and instead the method `askForGlobalPermissionUsingRequesterClass` was renamed appropriately. This was tested with my [test app](https://github.com/below/ExpoNotificationIssue).
thank you for the PR, @below! |
Thank you for filing this issue! |
Summary
On iOS, the permissions for Notification can be requested with a set of options. The most common ones are to display alerts or play sounds, but they can also enable notifications on CarPlay displays, or enable critical alerts. Over the course of its lifetime, an app may introduce aditional capabilities, such as critical alerts or other, newly introduced iOS features and thus, must call
requestAuthorization(options:completionHandler:)
again with a new set of options. Expo Notifications supports all these options forrequestPermissionsAsync
.The issue is, that after an app is newly installed on a device, the first call to
Notification.requestPermissionsAsync
will prompt the user, and set the given options. However, subsequent calls will have no effect.This means, for the user to take advantage of new features, they will have to de-install and re-install the app, which is completely unacceptable.
The Root of the Problem
The root of the problem can be found in EXPermissionsService.m:111:
This means that permissions are only ever requested again, if permission has not previously been granted. This however does not consider different options, but only if any sort of notification permissions have been granted. This means that if previously only Alerts have been requested, a new request for Alerts and Sounds will be ignored.
The Solution
On iOS, it is perfectly acceptable to call
requestAuthorization(options:completionHandler:)
repeatedly:Also not that several tutorials suggest to check the permission status first, and then requesting it. This is — at least for iOS — not advisable.
My proposal therefore is to remove the method
askForPermissionUsingRequesterClass
entirely, and instead renameaskForGlobalPermissionUsingRequesterClass
appropriately.I will gladly file an appropriate pull request
What platform(s) does this occur on?
iOS
Environment
expo-env-info 1.0.5 environment info:
System:
OS: macOS 13.0.1
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 18.7.0 - /opt/homebrew/bin/node
npm: 8.15.0 - /opt/homebrew/bin/npm
Managers:
CocoaPods: 1.11.3 - /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms: DriverKit 22.1, iOS 16.1, macOS 13.0, tvOS 16.1, watchOS 9.1
IDEs:
Android Studio: 2021.3 AI-213.7172.25.2113.9123335
Xcode: 14.1/14B47b - /usr/bin/xcodebuild
npmPackages:
expo: ~47.0.3 => 47.0.3
react: 18.1.0 => 18.1.0
react-native: 0.70.5 => 0.70.5
npmGlobalPackages:
expo-cli: 5.3.0
Expo Workflow: bare
Minimal reproducible example
A minimal reproducible example can be found here
The text was updated successfully, but these errors were encountered: