Skip to content

Instantly share code, notes, and snippets.

@walidvb
Last active November 13, 2019 21:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save walidvb/741186db857592706c93d82ea7a9616f to your computer and use it in GitHub Desktop.
Save walidvb/741186db857592706c93d82ea7a9616f to your computer and use it in GitHub Desktop.
This allows displaying a dialog to the user before actually requesting permissions from the OS. Ultimately, it allows to request that permission multiple times, which iOS prevents
import requestPermissions from './requestPermissions'
requestPermission(pickFromCamera, {
permissionNames: ['CAMERA', 'CAMERA_ROLL'],
alertTitle: '📷Camera📸',
alertBody: 'Use your camera now.',
})
// Only request every n times this is run
// useful to ask for notification permission
// every now and then 👍🏻
requestPermission(onGranted, {
permissionNames: ['NOTIFICATIONS'] ,
alertTitle: '🎶Notifications🎶',
alertBody: 'Get notified when your session has closed and never miss an opportunity to enter your contributions.',
askEvery: [0, 1, 5, 10, 20, 30, 50],
})
import * as Permissions from 'expo-permissions';
import { AsyncStorage, Platform, Alert } from 'react-native';
const key = (name) => `@YOUR-APP:${name}-request-status`
const countKey = (name) => `@YOUR-APP:${name}-request-count`
async function requestPermission(callback, {
permissionNames,
alertBody,
alertTitle,
askEvery
}) {
const statii = await Promise.all(permissionNames.map(async name => await Permissions.getAsync(Permissions[name])))
// only ask if permissions have not already been determined, because
// iOS won't necessarily prompt the user a second time.
if (statii.find(s => s !== 'granted')) {
return Platform.OS === 'ios' ? askLocal() : askForReal()
}
callback()
async function askSinglePermission(permissionName) {
const { status } = await Permissions.askAsync(Permissions[permissionName]);
return status
}
async function askForReal() {
const statii = await Promise.all(permissionNames.map(async name => await askSinglePermission(name)))
if (statii.find(s => s !== 'granted')) {
return;
}
return callback()
}
async function askLocal() {
if (await hasAcceptedLocal()) {
return askForReal()
}
// this only considers the first permission request count
// as i only know of one case where both would be needed.
const visitsCountKey = countKey(permissionNames[0])
const oldCountKey = await AsyncStorage.getItem(visitsCountKey)
const visitsCount = parseInt(oldCountKey || 0)
AsyncStorage.setItem(visitsCountKey, `${visitsCount + 1}`)
if (!askEvery || askEvery.includes(visitsCount)) {
Alert.alert(
alertTitle, alertBody,
[
{
text: 'Maybe later',
onPress: cancel,
style: 'cancel',
},
{
text: 'Allow',
onPress: acceptLocal
},
])
function cancel() {
}
}
}
// as iOS returns 'undetermined' for denied permissions,
// we have no way to know whether it was denied or not.
// therefore, we assume that if Local was granted
// native also was
async function hasAcceptedLocal() {
const all = await Promise.all(permissionNames.map(async (name) => (await AsyncStorage.getItem(key(name))) === '1'))
return !all.includes(false)
}
async function acceptLocal() {
permissionNames.map(async name => AsyncStorage.setItem(key(name), '1'))
askForReal()
}
}
export default requestPermission
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment