Expo Camera provides comprehensive permission management for camera and microphone access through React hooks and direct async functions. This ensures proper user consent and handles platform-specific permission requirements.
React hook for managing camera permissions with automatic status monitoring.
/**
* Hook for camera permission management
* @param options Optional configuration for permission requests
* @returns Tuple containing [status, requestPermission, getPermission]
*/
const useCameraPermissions: (
options?: PermissionHookOptions<object>
) => [
PermissionResponse | null,
() => Promise<PermissionResponse>,
() => Promise<PermissionResponse>
];React hook for managing microphone permissions (required for video recording with audio).
/**
* Hook for microphone permission management
* @param options Optional configuration for permission requests
* @returns Tuple containing [status, requestPermission, getPermission]
*/
const useMicrophonePermissions: (
options?: PermissionHookOptions<object>
) => [
PermissionResponse | null,
() => Promise<PermissionResponse>,
() => Promise<PermissionResponse>
];Async functions for direct permission management without hooks.
/**
* Check current camera permission status
* @returns Promise resolving to current permission response
*/
function getCameraPermissionsAsync(): Promise<PermissionResponse>;
/**
* Request camera permissions from the user
* On iOS requires NSCameraUsageDescription in Info.plist
* @returns Promise resolving to permission response after user interaction
*/
function requestCameraPermissionsAsync(): Promise<PermissionResponse>;
/**
* Check current microphone permission status
* @returns Promise resolving to current permission response
*/
function getMicrophonePermissionsAsync(): Promise<PermissionResponse>;
/**
* Request microphone permissions from the user
* On iOS requires NSMicrophoneUsageDescription in Info.plist
* @returns Promise resolving to permission response after user interaction
*/
function requestMicrophonePermissionsAsync(): Promise<PermissionResponse>;For backward compatibility, all permission functions are also available through the legacy Camera object.
/**
* Legacy Camera object containing permission methods and scanFromURLAsync
* @deprecated Use named exports instead
*/
const Camera = {
/**
* Check current camera permission status
* @returns Promise resolving to current permission response
*/
getCameraPermissionsAsync(): Promise<PermissionResponse>;
/**
* Request camera permissions from the user
* @returns Promise resolving to permission response after user interaction
*/
requestCameraPermissionsAsync(): Promise<PermissionResponse>;
/**
* Check current microphone permission status
* @returns Promise resolving to current permission response
*/
getMicrophonePermissionsAsync(): Promise<PermissionResponse>;
/**
* Request microphone permissions from the user
* @returns Promise resolving to permission response after user interaction
*/
requestMicrophonePermissionsAsync(): Promise<PermissionResponse>;
/**
* Scan barcodes from image URL
* @param url URL of the image to scan
* @param barcodeTypes Array of barcode types (defaults to ['qr'])
* @returns Promise resolving to array of detected barcodes
*/
scanFromURLAsync(url: string, barcodeTypes?: BarcodeType[]): Promise<BarcodeScanningResult[]>;
};Core types for permission management.
interface PermissionResponse {
/**
* Current permission status
*/
status: PermissionStatus;
/**
* When the permission expires (if applicable)
*/
expires: PermissionExpiration;
/**
* Whether permission can be asked again
*/
canAskAgain: boolean;
/**
* Whether permission has been granted
*/
granted: boolean;
}
type PermissionStatus =
| 'undetermined'
| 'granted'
| 'denied';
type PermissionExpiration = 'never' | number;
interface PermissionHookOptions<T> {
/**
* Request permission immediately when hook is called
*/
request?: boolean;
/**
* Additional data to pass with permission request
*/
data?: T;
}import React, { useEffect } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { CameraView, useCameraPermissions } from 'expo-camera';
export function CameraWithPermissions() {
const [permission, requestPermission] = useCameraPermissions();
if (!permission) {
// Camera permissions are still loading
return (
<View style={styles.container}>
<Text>Loading permissions...</Text>
</View>
);
}
if (!permission.granted) {
// Camera permissions are not granted yet
return (
<View style={styles.container}>
<Text style={styles.message}>
We need your permission to show the camera
</Text>
<TouchableOpacity style={styles.button} onPress={requestPermission}>
<Text style={styles.buttonText}>Grant Permission</Text>
</TouchableOpacity>
{!permission.canAskAgain && (
<Text style={styles.warning}>
Please enable camera permissions in device settings
</Text>
)}
</View>
);
}
return (
<View style={styles.container}>
<CameraView style={styles.camera} facing="back" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
message: {
textAlign: 'center',
paddingBottom: 10,
},
camera: {
flex: 1,
},
button: {
padding: 10,
backgroundColor: '#007AFF',
margin: 20,
borderRadius: 5,
},
buttonText: {
color: 'white',
textAlign: 'center',
},
warning: {
textAlign: 'center',
color: 'red',
fontSize: 12,
},
});import React, { useState, useRef } from 'react';
import { View, TouchableOpacity, Text, Alert, StyleSheet } from 'react-native';
import {
CameraView,
useCameraPermissions,
useMicrophonePermissions,
CameraRecordingOptions
} from 'expo-camera';
export function VideoRecordingCamera() {
const [isRecording, setIsRecording] = useState(false);
const [cameraPermission, requestCameraPermission] = useCameraPermissions();
const [microphonePermission, requestMicrophonePermission] = useMicrophonePermissions();
const cameraRef = useRef<CameraView>(null);
const checkPermissions = async () => {
if (!cameraPermission?.granted) {
const result = await requestCameraPermission();
if (!result.granted) {
Alert.alert('Camera permission is required');
return false;
}
}
if (!microphonePermission?.granted) {
const result = await requestMicrophonePermission();
if (!result.granted) {
Alert.alert('Microphone permission is required for video with audio');
return false;
}
}
return true;
};
const startRecording = async () => {
if (!cameraRef.current || !(await checkPermissions())) return;
try {
setIsRecording(true);
const recordingOptions: CameraRecordingOptions = {
maxDuration: 60, // 60 seconds max
maxFileSize: 100 * 1024 * 1024, // 100MB max
};
const video = await cameraRef.current.record(recordingOptions);
console.log('Video recorded:', video.uri);
} catch (error) {
Alert.alert('Recording failed', error.message);
} finally {
setIsRecording(false);
}
};
const stopRecording = async () => {
if (cameraRef.current && isRecording) {
await cameraRef.current.stopRecording();
setIsRecording(false);
}
};
if (!cameraPermission || !microphonePermission) {
return (
<View style={styles.container}>
<Text>Loading permissions...</Text>
</View>
);
}
return (
<View style={styles.container}>
<CameraView
ref={cameraRef}
style={styles.camera}
facing="back"
mode="video"
/>
<View style={styles.controls}>
<TouchableOpacity
style={[styles.button, isRecording && styles.recordingButton]}
onPress={isRecording ? stopRecording : startRecording}
>
<Text style={styles.buttonText}>
{isRecording ? 'Stop Recording' : 'Start Recording'}
</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
camera: { flex: 1 },
controls: {
position: 'absolute',
bottom: 50,
left: 0,
right: 0,
alignItems: 'center',
},
button: {
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 25,
},
recordingButton: {
backgroundColor: '#FF3B30',
},
buttonText: {
color: 'white',
fontWeight: 'bold',
},
});import React, { useEffect, useState } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import {
getCameraPermissionsAsync,
requestCameraPermissionsAsync,
PermissionResponse
} from 'expo-camera';
export function DirectPermissionExample() {
const [cameraPermission, setCameraPermission] = useState<PermissionResponse | null>(null);
useEffect(() => {
checkCameraPermissions();
}, []);
const checkCameraPermissions = async () => {
try {
const permission = await getCameraPermissionsAsync();
setCameraPermission(permission);
} catch (error) {
console.error('Failed to check camera permissions:', error);
}
};
const requestCameraPermissions = async () => {
try {
const permission = await requestCameraPermissionsAsync();
setCameraPermission(permission);
if (permission.granted) {
console.log('Camera permission granted');
} else if (!permission.canAskAgain) {
console.log('User denied permission permanently');
} else {
console.log('User denied permission');
}
} catch (error) {
console.error('Failed to request camera permissions:', error);
}
};
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
<Text style={{ marginBottom: 20 }}>
Camera Permission Status: {cameraPermission?.status || 'Unknown'}
</Text>
<Text style={{ marginBottom: 20 }}>
Granted: {cameraPermission?.granted ? 'Yes' : 'No'}
</Text>
<Text style={{ marginBottom: 20 }}>
Can Ask Again: {cameraPermission?.canAskAgain ? 'Yes' : 'No'}
</Text>
{!cameraPermission?.granted && (
<TouchableOpacity
style={{
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 5,
alignItems: 'center',
}}
onPress={requestCameraPermissions}
>
<Text style={{ color: 'white' }}>Request Camera Permission</Text>
</TouchableOpacity>
)}
</View>
);
}import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import {
useCameraPermissions,
useMicrophonePermissions,
PermissionResponse
} from 'expo-camera';
export function PermissionMonitor() {
const [cameraPermission, requestCameraPermission, getCameraPermission] = useCameraPermissions();
const [microphonePermission, requestMicrophonePermission, getMicrophonePermission] = useMicrophonePermissions();
// Monitor permission changes
useEffect(() => {
const intervalId = setInterval(async () => {
// Manually refresh permissions if needed
await getCameraPermission();
await getMicrophonePermission();
}, 5000); // Check every 5 seconds
return () => clearInterval(intervalId);
}, [getCameraPermission, getMicrophonePermission]);
const renderPermissionStatus = (permission: PermissionResponse | null, name: string) => (
<View style={{ marginBottom: 20 }}>
<Text style={{ fontWeight: 'bold' }}>{name} Permission:</Text>
<Text>Status: {permission?.status || 'Loading...'}</Text>
<Text>Granted: {permission?.granted ? 'Yes' : 'No'}</Text>
<Text>Can Ask Again: {permission?.canAskAgain ? 'Yes' : 'No'}</Text>
<Text>Expires: {permission?.expires === 'never' ? 'Never' : permission?.expires}</Text>
</View>
);
return (
<View style={{ flex: 1, padding: 20 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 20 }}>
Permission Status Monitor
</Text>
{renderPermissionStatus(cameraPermission, 'Camera')}
{renderPermissionStatus(microphonePermission, 'Microphone')}
</View>
);
}import React from 'react';
import { View, Text, TouchableOpacity, Alert } from 'react-native';
import { useCameraPermissions } from 'expo-camera';
export function PermissionErrorHandling() {
const [permission, requestPermission] = useCameraPermissions();
const handlePermissionRequest = async () => {
try {
const result = await requestPermission();
if (result.granted) {
Alert.alert('Success', 'Camera permission granted');
} else if (!result.canAskAgain) {
Alert.alert(
'Permission Denied',
'Camera permission was permanently denied. Please enable it in device settings.',
[
{ text: 'Cancel', style: 'cancel' },
{ text: 'Open Settings', onPress: () => {
// Open device settings - platform specific implementation needed
console.log('Would open device settings');
}}
]
);
} else {
Alert.alert('Permission Required', 'Camera access is needed to take photos');
}
} catch (error) {
Alert.alert('Error', `Failed to request permission: ${error.message}`);
}
};
return (
<View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
{permission?.granted ? (
<Text style={{ textAlign: 'center', fontSize: 18 }}>
Camera permission is granted! 📸
</Text>
) : (
<TouchableOpacity
style={{
backgroundColor: '#007AFF',
padding: 15,
borderRadius: 5,
alignItems: 'center',
}}
onPress={handlePermissionRequest}
>
<Text style={{ color: 'white' }}>Request Camera Permission</Text>
</TouchableOpacity>
)}
</View>
);
}