Permissions — The Right Way
iOS and Android both require explicit user consent before accessing sensitive device features. Getting permissions right means requesting at the right time (in context, not at startup), handling all three states (granted / denied / blocked), and explaining why your app needs the permission.
Permission Request Pattern
import * as Location from 'expo-location';
import * as Camera from 'expo-camera';
import * as Notifications from 'expo-notifications';
import { Linking, Alert } from 'react-native';
// ---- Generic permission hook ----
type PermStatus = 'undetermined' | 'granted' | 'denied';
interface PermissionOptions {
request: () => Promise<{ status: string }>;
check: () => Promise<{ status: string }>;
featureName: string;
}
export function usePermission({ request, check, featureName }: PermissionOptions) {
const [status, setStatus] = React.useState<PermStatus>('undetermined');
useEffect(() => {
check().then(result => setStatus(result.status as PermStatus));
}, []);
async function requestPermission(): Promise<boolean> {
const result = await request();
if (result.status === 'granted') {
setStatus('granted');
return true;
}
setStatus('denied');
if (result.status === 'denied') {
// Platform may have shown "Don't ask again" — must send user to Settings
Alert.alert(
`${featureName} Permission Required`,
`Please enable ${featureName} access in your device settings for this feature to work.`,
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Open Settings', onPress: () => Linking.openSettings() },
]
);
}
return false;
}
return { status, requestPermission, isGranted: status === 'granted' };
}
// ---- Specialized hooks using the generic pattern ----
export function useCameraPermission() {
return usePermission({
request: Camera.requestCameraPermissionsAsync,
check: Camera.getCameraPermissionsAsync,
featureName: 'Camera',
});
}
export function useLocationPermission() {
return usePermission({
request: Location.requestForegroundPermissionsAsync,
check: Location.getForegroundPermissionsAsync,
featureName: 'Location',
});
}
// ---- Best practice: request in context, not at startup ----
function LocationButton() {
const { isGranted, requestPermission } = useLocationPermission();
async function handlePress() {
if (isGranted) {
showUserLocation(); // go straight to feature
} else {
const granted = await requestPermission(); // only asks when user taps button
if (granted) showUserLocation();
}
}
return (
<Pressable onPress={handlePress} accessibilityRole="button" accessibilityLabel="Show my location">
<Text>📍 Show my location</Text>
</Pressable>
);
}
function showUserLocation() {}Android Permissions (React Native CLI)
import { PermissionsAndroid, Platform } from 'react-native';
// For React Native CLI (non-Expo), Android permissions need explicit request
async function requestAndroidCameraPermission() {
if (Platform.OS !== 'android') return true;
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Camera Permission',
message: 'This app needs camera access to scan barcodes',
buttonPositive: 'Allow',
buttonNegative: 'Deny',
}
);
return granted === PermissionsAndroid.RESULTS.GRANTED;
}
// Must declare in AndroidManifest.xml:
// <uses-permission android:name="android.permission.CAMERA" />
// <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />Tip
Tip
Practice Permissions The Right Way in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
React Native bridges JavaScript and native platform code
Practice Task
Note
Practice Task — (1) Write a working example of Permissions The Right Way from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with Permissions The Right Way is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready react native code.