mediumReact Native#21

Camera roll permission request

Prompt

Use expo-image-picker (or react-native PermissionsAndroid) to request camera permission. If granted, open the camera; if denied, show an alert directing the user to Settings.

Solution

const [permission, requestPermission] = useCameraPermissions()
const handlePress = async () => {
  if (!permission?.granted) {
    const result = await requestPermission()
    if (!result.granted) {
      Alert.alert('Permission needed', 'Open Settings to enable camera access')
      return
    }
  }
  await ImagePicker.launchCameraAsync()
}
return <Button title="Open Camera" onPress={handlePress} />
Mentor's take

Permissions are a one-shot UX resource: both platforms show the system prompt essentially once, and after a hard denial only the user flipping it in Settings can recover. That's why the architecture is check → request → handle denial, triggered by the feature — the request fires when the user taps "Open Camera," so the prompt arrives with context and the grant rate is highest. Requesting at app launch burns the one prompt with zero context.

The code walks the full state machine: check permission?.granted first (never re-prompt someone who already granted), request only when needed, and treat denial as a real state — the Settings alert exists because asking again is literally impossible once blocked. In production that alert's action button calls Linking.openSettings() to deep-link the user directly there. Underneath the Expo hook, the platforms differ — iOS requires an NSCameraUsageDescription purpose string in Info.plist (the app is killed without it) and Android runtime permissions have the rationale/permanent-denial dance — but useCameraPermissions normalizes both behind one status object, which is exactly the kind of platform-divergence-hidden-behind-a-hook worth naming.

Red flag: requesting every permission on app launch. It inflates denial rates and signals you've never watched the funnel; ask lazily, at the moment of need, with rationale.

Say it: "Permissions are check-then-request at the moment of use — denied-once is a UI state I design for, and blocked means a Settings deep link, never another prompt."