Permission management in Expo apps is an important part of the development process, especially when dealing with sensitive features like camera, location, microphone, etc. Expo provides a unified permission management API that simplifies the cross-platform permission request process.
Permission Management Basics:
Expo uses expo-permissions and permission APIs from various modules to manage app permissions.
Install Permission Module:
bashnpx expo install expo-permissions
Basic Permission Request Flow:
typescriptimport * as Permissions from 'expo-permissions'; import { Camera } from 'expo-camera'; async function requestCameraPermission() { // Request camera permission const { status } = await Camera.requestCameraPermissionsAsync(); if (status === 'granted') { console.log('Camera permission granted'); } else { console.log('Camera permission denied'); } }
Common Permission Types:
- Camera Permission
typescriptimport { Camera } from 'expo-camera'; // Request camera permission const { status } = await Camera.requestCameraPermissionsAsync(); // Check permission status const { status: currentStatus } = await Camera.getCameraPermissionsAsync(); // Request microphone permission (for video recording) const { status: audioStatus } = await Camera.requestMicrophonePermissionsAsync();
- Location Permission
typescriptimport * as Location from 'expo-location'; // Request foreground location permission const { status } = await Location.requestForegroundPermissionsAsync(); // Request background location permission const { status: backgroundStatus } = await Location.requestBackgroundPermissionsAsync(); // Get current location const location = await Location.getCurrentPositionAsync({});
- Notification Permission
typescriptimport * as Notifications from 'expo-notifications'; // Request notification permission const { status } = await Notifications.requestPermissionsAsync(); // Configure notification handler Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowAlert: true, shouldPlaySound: false, shouldSetBadge: false, }), });
- Media Library Permission
typescriptimport * as MediaLibrary from 'expo-media-library'; // Request media library permission const { status } = await MediaLibrary.requestPermissionsAsync(); // Save image to media library const asset = await MediaLibrary.createAssetAsync(uri);
- Contacts Permission
typescriptimport * as Contacts from 'expo-contacts'; // Request contacts permission const { status } = await Contacts.requestPermissionsAsync(); // Get contacts const { data } = await Contacts.getContactsAsync();
- Calendar Permission
typescriptimport * as Calendar from 'expo-calendar'; // Request calendar permission const { status } = await Calendar.requestCalendarPermissionsAsync(); // Create calendar event const eventId = await Calendar.createEventAsync(calendarId, eventDetails);
Permission Status:
Permission request returns status including:
granted: Permission granteddenied: Permission deniedundetermined: User hasn't made a choice yetlimited: Partial permission granted (iOS specific)
Permission Configuration:
Declare permissions in app.json:
json{ "expo": { "ios": { "infoPlist": { "NSCameraUsageDescription": "Need camera permission to take photos", "NSLocationWhenInUseUsageDescription": "Need location permission to show nearby information", "NSMicrophoneUsageDescription": "Need microphone permission to record audio" } }, "android": { "permissions": [ "CAMERA", "ACCESS_FINE_LOCATION", "RECORD_AUDIO", "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE" ] } } }
Best Practices:
- Request Permissions at Appropriate Time
typescript// Request permission when user needs to use the feature function CameraButton() { const [hasPermission, setHasPermission] = useState(null); useEffect(() => { (async () => { const { status } = await Camera.requestCameraPermissionsAsync(); setHasPermission(status === 'granted'); })(); }, []); if (hasPermission === null) { return <Text>Requesting permission...</Text>; } if (hasPermission === false) { return <Text>No camera permission</Text>; } return <Button title="Open Camera" onPress={openCamera} />; }
- Provide Clear Permission Explanations
typescriptasync function requestPermissionWithExplanation() { const { status } = await Location.requestForegroundPermissionsAsync(); if (status !== 'granted') { Alert.alert( 'Location Permission Required', 'App needs location permission to show nearby information. Please grant permission in settings.', [ { text: 'Cancel', style: 'cancel' }, { text: 'Open Settings', onPress: () => Linking.openSettings() } ] ); } }
- Handle Permission Denial
typescriptasync function handlePermissionDenied() { const { status } = await Camera.requestCameraPermissionsAsync(); if (status !== 'granted') { // Check if can ask again const { canAskAgain } = await Camera.getCameraPermissionsAsync(); if (canAskAgain) { Alert.alert( 'Camera Permission Required', 'App needs camera permission for photo feature', [ { text: 'Cancel' }, { text: 'Grant Permission', onPress: () => requestCameraPermission() } ] ); } else { Alert.alert( 'Permission Permanently Denied', 'Please manually grant permission in system settings', [ { text: 'Cancel' }, { text: 'Open Settings', onPress: () => Linking.openSettings() } ] ); } } }
- Permission Status Caching
typescriptimport { useState, useEffect } from 'react'; function usePermission(permissionGetter) { const [status, setStatus] = useState(null); useEffect(() => { (async () => { const { status } = await permissionGetter(); setStatus(status); })(); }, [permissionGetter]); return status; } // Usage const cameraStatus = usePermission(() => Camera.getCameraPermissionsAsync());
Platform Differences:
- iOS Permissions
- Need to declare usage purpose in Info.plist
- Some permissions can only be requested once
- Users can change permissions in settings at any time
- Android Permissions
- Need to declare in AndroidManifest.xml
- Can request permissions multiple times
- Runtime permissions started from Android 6.0
Common Issues:
- Permission Request Fails
- Check if permission is declared in config file
- Ensure using correct permission API
- Handle user permission denial
- Inconsistent Permission Status
- Cache permission status
- Recheck permissions when needed
- Handle permission status changes
- Background Permissions
- Background location permissions need special handling
- Notification background permissions need additional configuration
- Follow platform-specific background permission rules
Security Considerations:
- Principle of Least Privilege: Only request necessary permissions
- Transparency: Clearly explain why permissions are needed
- User Control: Allow users to revoke permissions
- Data Protection: Properly handle sensitive data
Good permission management not only improves user experience but also ensures the app complies with privacy policies and legal regulations of various platforms.