expo: Handle Rejection for Remote Notification Permission

I am trying to handle the case when users “Don’t Allow” push notifications when being asked for the permission. However, in the following code, the console.log only works if when users actually allow the permission.

const { status } = await Permissions.askAsync(Permissions.REMOTE_NOTIFICATIONS);
console.log("status",status);

I am not sure what causes it. It seems to me the app just keep waiting when users reject the permission.

“expo”: “^15.1.2”,

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 30 (8 by maintainers)

Most upvoted comments

Hey @terribleben. I am using expo sdk 18. If the user denies notification permission, he is not prompted again for permissions. I believe this has to be with the implementation on Apple’s side. However, even when i manually go and change the notification settings, the const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS); still returns undetermined. Shouldn’t it return granted in this case?

This is still an issue in Expo 25.

  1. User denies permissions.
  2. Permissions.askAsync() resolves { status: "denied", expires: "never" }
  3. Permissions get set iOS side and I assume they “never” expire, thus askAsync() can never trigger again (since I’m assuming expires is tested first).

This is a problem with askAsync() auto-setting “never” (even if the permission is denied). It needs to only do that if status resolves to granted. If status resolves to denied then just set the expires to now + 1ms, or something.

This way we constantly pester people to give us perms.

The askAsync() api could be expanded upon by accepting an expiration and just defaulting to now-or-never™ expiration.

Please reopen until there’s a fix or official workaround to this. Seems like a lot of people are still running into this problem where if someone initially denies permissions, their app is useless until a reinstall or if the end-user is tech savvy enough to track down these permissions in iOS settings and update them.

P.S. I don’t know if it works for other perms, but mines broken for CONTACTS.

Alert dialog is not shown, when user rejects a permission. How to handle this scenario?

In the meantime, everyone can do a check for those perms up front and offer a helpful message to the user. Something like:

handlePermRequest = () => {
  const { status } = await Permissions.askAsync(Permissions.CONTACTS)

  if (status !== 'granted') {
    Alert.alert(`You need to give us permission(s) for the ABC app to work! Go into your system settings, find the ABC app, and enable XYZ permissions.`)
  }
}

@terribleben It would be really great if you guys can fix this issue soon.

I was also trying to open the settings app if the status was denied with Linking.openURL('app-settings:') but that didn’t work, maybe you can look into adding https://github.com/lunarmayor/react-native-open-settings @terribleben.

@ChenLi0830 sorry about this, it’s a combination of an annoyingly designed Apple API and some omissions in our SDK code as well.

  • First, Apple doesn’t distinguish between “permissions were rejected” and “we don’t know what the permissions are yet.”
  • Second, Apple doesn’t guarantee they will invoke any callback if the permissions were previously rejected.
  • Third, we don’t handle this possibility very well in our SDK right now.

As a result, rejection followed by subsequent calls to askAsync may lead to the promise hanging forever, as you’ve seen. This is something I’d like to fix soon or at least provide better docs for.

In the meantime, my recommendation is to use a key in AsyncStorage to manually keep track of whether you’ve ever asked for permissions before. Then your code would look something like this pseudocode:

const haveWeAskedBefore = await myGetPermissionsAskedBeforeMethod();
const { existingStatus } = await Permissions.getAsync(Permissions.REMOTE_NOTIFICATIONS);
let finalStatus = existingStatus;

if (existingStatus !== 'granted' && !haveWeAskedBefore) {
  const { status } = await Permissions.askAsync(Permissions.REMOTE_NOTIFICATIONS);
  await mySetPermissionsAskedBeforeMethod(true);
  finalStatus = status;
}