# ☝🏼 React Native Permissions [![npm version](https://badge.fury.io/js/react-native-permissions.svg)](https://badge.fury.io/js/react-native-permissions) [![npm](https://img.shields.io/npm/dt/react-native-permissions.svg)](https://www.npmjs.org/package/react-native-permissions) ![Platform - Android and iOS](https://img.shields.io/badge/platform-Android%20%7C%20iOS-yellow.svg) ![MIT](https://img.shields.io/dub/l/vibe-d.svg) [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier) An unified permissions API for React Native on iOS and Android. ## Support | version | react-native version | | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | | 2.0.0+ | 0.60.0+ | | 2.0.0+ & [`jetify -r`](https://github.com/mikehardy/jetifier/blob/master/README.md#to-reverse-jetify--convert-node_modules-dependencies-to-support-libraries) | 0.56.0 - 0.59.10 | ## Setup ```bash $ npm install --save react-native-permissions # --- or --- $ yarn add react-native-permissions ``` ### iOS By default no permission handler is installed. Update your `Podfile` by choosing the ones you want to check or request, then run `pod install`. ```ruby target 'YourAwesomeProject' do # … permissions_path = '../node_modules/react-native-permissions/ios' pod 'Permission-BluetoothPeripheral', :path => "#{permissions_path}/BluetoothPeripheral.podspec" pod 'Permission-Calendars', :path => "#{permissions_path}/Calendars.podspec" pod 'Permission-Camera', :path => "#{permissions_path}/Camera.podspec" pod 'Permission-Contacts', :path => "#{permissions_path}/Contacts.podspec" pod 'Permission-FaceID', :path => "#{permissions_path}/FaceID.podspec" pod 'Permission-LocationAlways', :path => "#{permissions_path}/LocationAlways.podspec" pod 'Permission-LocationWhenInUse', :path => "#{permissions_path}/LocationWhenInUse.podspec" pod 'Permission-MediaLibrary', :path => "#{permissions_path}/MediaLibrary.podspec" pod 'Permission-Microphone', :path => "#{permissions_path}/Microphone.podspec" pod 'Permission-Motion', :path => "#{permissions_path}/Motion.podspec" pod 'Permission-Notifications', :path => "#{permissions_path}/Notifications.podspec" pod 'Permission-PhotoLibrary', :path => "#{permissions_path}/PhotoLibrary.podspec" pod 'Permission-Reminders', :path => "#{permissions_path}/Reminders.podspec" pod 'Permission-Siri', :path => "#{permissions_path}/Siri.podspec" pod 'Permission-SpeechRecognition', :path => "#{permissions_path}/SpeechRecognition.podspec" pod 'Permission-StoreKit', :path => "#{permissions_path}/StoreKit.podspec" end ``` Then update your `Info.plist` with wanted permissions usage descriptions: ```xml NSAppleMusicUsageDescription YOUR TEXT NSBluetoothAlwaysUsageDescription YOUR TEXT NSBluetoothPeripheralUsageDescription YOUR TEXT NSCalendarsUsageDescription YOUR TEXT NSCameraUsageDescription YOUR TEXT NSContactsUsageDescription YOUR TEXT NSFaceIDUsageDescription YOUR TEXT NSLocationAlwaysAndWhenInUseUsageDescription YOUR TEXT NSLocationAlwaysUsageDescription YOUR TEXT NSLocationWhenInUseUsageDescription YOUR TEXT NSMicrophoneUsageDescription YOUR TEXT NSMotionUsageDescription YOUR TEXT NSPhotoLibraryUsageDescription YOUR TEXT NSRemindersUsageDescription YOUR TEXT NSSpeechRecognitionUsageDescription YOUR TEXT ``` ### Android Add all wanted permissions to your app `android/app/src/main/res/AndroidManifest.xml` file: ```xml ``` ## πŸ†˜ Manual linking Because this package targets React Native 0.60.0+, you will probably don't need to link it manually. Otherwise if it's not the case, follow this additional instructions:
πŸ‘€ See manual linking instructions ### iOS Add this line to your `ios/Podfile` file, then run `pod install`. ```bash target 'YourAwesomeProject' do # … pod 'RNPermissions', :path => '../node_modules/react-native-permissions' end ``` ### Android 1. Add the following lines to `android/settings.gradle`: ```gradle include ':react-native-permissions' project(':react-native-permissions').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-permissions/android') ``` 2. Add the implementation line to the dependencies in `android/app/build.gradle`: ```gradle dependencies { // ... implementation project(':react-native-permissions') } ``` 3. Add the import and link the package in `MainApplication.java`: ```java import com.reactnativecommunity.rnpermissions.RNPermissionsPackage; // <- add the RNPermissionsPackage import public class MainApplication extends Application implements ReactApplication { // … @Override protected List getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); // … packages.add(new RNPermissionsPackage()); return packages; } // … } ```
## Understanding permission flow As permissions are not handled in the same way on iOS and Android, this library provides an abstraction over the two platforms behaviors. To understand it a little better, take a look to these two flowcharts: ### iOS flow ``` ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ check(PERMISSIONS.IOS.CAMERA) ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ β”‚ Is the feature available on this deviceΒ ? β”‚ ╔════╗ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•‘ NO ║──────────────┐ β”‚ β•šβ•β•β•β•β• β”‚ ╔═════╗ β–Ό β•‘ YES β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•šβ•β•β•β•β•β• β”‚ RESULTS.UNAVAILABLE β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Is the permission requestableΒ ? β”‚ ╔════╗ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•‘ NO ║──────────────┐ β”‚ β•šβ•β•β•β•β• β”‚ ╔═════╗ β–Ό β•‘ YES β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•šβ•β•β•β•β•β• β”‚ RESULTS.BLOCKED / β”‚ β”‚ β”‚ RESULTS.GRANTED β”‚ β–Ό β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ RESULTS.DENIED β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β–Ό ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ request(PERMISSIONS.IOS.CAMERA) ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ β”‚ Does the user accepted the requestΒ ? β”‚ ╔════╗ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•‘ NO ║──────────────┐ β”‚ β•šβ•β•β•β•β• β”‚ ╔═════╗ β–Ό β•‘ YES β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•šβ•β•β•β•β•β• β”‚ RESULTS.BLOCKED β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ RESULTS.GRANTED β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Android flow ``` ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┃ check(PERMISSIONS.ANDROID.CAMERA) ┃ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ β”‚ Is the feature available on this deviceΒ ? β”‚ ╔════╗ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•‘ NO ║──────────────┐ β”‚ β•šβ•β•β•β•β• β”‚ ╔═════╗ β–Ό β•‘ YES β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•šβ•β•β•β•β•β• β”‚ RESULTS.UNAVAILABLE β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ Is the permission requestableΒ ? β”‚ ╔════╗ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•‘ NO ║──────────────┐ β”‚ β•šβ•β•β•β•β• β”‚ ╔═════╗ β–Ό β•‘ YES β•‘ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β•šβ•β•β•β•β•β• β”‚ RESULTS.BLOCKED / β”‚ β”‚ β”‚ RESULTS.GRANTED β”‚ β–Ό β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ RESULTS.DENIED │◀──────────────────────┐ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β–Ό β”‚ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ╔════╗ ┃ request(PERMISSIONS.ANDROID.CAMERA) ┃ β•‘ NO β•‘ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ β•šβ•β•β•β•β• β”‚ β”‚ Does the user accepted β”‚ the requestΒ ? β”‚ β”‚ ╔════╗ Does the user checked β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•‘ NO ║─────"NeverΒ askΒ again"Β ? β”‚ β•šβ•β•β•β•β• β”‚ ╔═════╗ ╔═════╗ β•‘ YES β•‘ β•‘ YES β•‘ β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β• β”‚ β”‚ β–Ό β–Ό β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ RESULTS.GRANTED β”‚ β”‚ RESULTS.BLOCKED β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## API ### Supported permissions ```js import {PERMISSIONS} from 'react-native-permissions'; // Android permissions PERMISSIONS.ANDROID.ACCEPT_HANDOVER; PERMISSIONS.ANDROID.ACCESS_BACKGROUND_LOCATION; PERMISSIONS.ANDROID.ACCESS_COARSE_LOCATION; PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION; PERMISSIONS.ANDROID.ACTIVITY_RECOGNITION; PERMISSIONS.ANDROID.ADD_VOICEMAIL; PERMISSIONS.ANDROID.ANSWER_PHONE_CALLS; PERMISSIONS.ANDROID.BODY_SENSORS; PERMISSIONS.ANDROID.CALL_PHONE; PERMISSIONS.ANDROID.CAMERA; PERMISSIONS.ANDROID.GET_ACCOUNTS; PERMISSIONS.ANDROID.PROCESS_OUTGOING_CALLS; PERMISSIONS.ANDROID.READ_CALENDAR; PERMISSIONS.ANDROID.READ_CALL_LOG; PERMISSIONS.ANDROID.READ_CONTACTS; PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE; PERMISSIONS.ANDROID.READ_PHONE_NUMBERS; PERMISSIONS.ANDROID.READ_PHONE_STATE; PERMISSIONS.ANDROID.READ_SMS; PERMISSIONS.ANDROID.RECEIVE_MMS; PERMISSIONS.ANDROID.RECEIVE_SMS; PERMISSIONS.ANDROID.RECEIVE_WAP_PUSH; PERMISSIONS.ANDROID.RECORD_AUDIO; PERMISSIONS.ANDROID.SEND_SMS; PERMISSIONS.ANDROID.USE_SIP; PERMISSIONS.ANDROID.WRITE_CALENDAR; PERMISSIONS.ANDROID.WRITE_CALL_LOG; PERMISSIONS.ANDROID.WRITE_CONTACTS; PERMISSIONS.ANDROID.WRITE_EXTERNAL_STORAGE; // iOS permissions PERMISSIONS.IOS.BLUETOOTH_PERIPHERAL; PERMISSIONS.IOS.CALENDARS; PERMISSIONS.IOS.CAMERA; PERMISSIONS.IOS.CONTACTS; PERMISSIONS.IOS.FACE_ID; PERMISSIONS.IOS.LOCATION_ALWAYS; PERMISSIONS.IOS.LOCATION_WHEN_IN_USE; PERMISSIONS.IOS.MEDIA_LIBRARY; PERMISSIONS.IOS.MICROPHONE; PERMISSIONS.IOS.MOTION; PERMISSIONS.IOS.PHOTO_LIBRARY; PERMISSIONS.IOS.REMINDERS; PERMISSIONS.IOS.SIRI; PERMISSIONS.IOS.SPEECH_RECOGNITION; PERMISSIONS.IOS.STOREKIT; ``` ### Permissions statuses Permission checks and requests resolve into one of these statuses: | Return value | Notes | | --------------------- | ----------------------------------------------------------------- | | `RESULTS.UNAVAILABLE` | This feature is not available (on this device / in this context) | | `RESULTS.DENIED` | The permission has not been requested / is denied but requestable | | `RESULTS.GRANTED` | The permission is granted | | `RESULTS.BLOCKED` | The permission is denied and not requestable anymore | ### Methods ```ts // type used in usage examples type PermissionStatus = 'unavailable' | 'denied' | 'blocked' | 'granted'; ``` #### check Check one permission status. ```ts function check(permission: string): Promise; ``` ```js import {check, PERMISSIONS, RESULTS} from 'react-native-permissions'; check(PERMISSIONS.IOS.LOCATION_ALWAYS) .then(result => { switch (result) { case RESULTS.UNAVAILABLE: console.log( 'This feature is not available (on this device / in this context)', ); break; case RESULTS.DENIED: console.log( 'The permission has not been requested / is denied but requestable', ); break; case RESULTS.GRANTED: console.log('The permission is granted'); break; case RESULTS.BLOCKED: console.log('The permission is denied and not requestable anymore'); break; } }) .catch(error => { // … }); ``` --- #### request Request one permission. ```ts type Rationale = { title: string; message: string; buttonPositive?: string; buttonNegative?: string; buttonNeutral?: string; }; function request( permission: string, rationale?: Rationale, ): Promise; ``` ```js import {request, PERMISSIONS} from 'react-native-permissions'; request(PERMISSIONS.IOS.LOCATION_ALWAYS).then(result => { // … }); ``` --- #### checkNotifications Check notifications permission status and get notifications settings values. ```ts interface NotificationSettings { // properties only availables on iOS // unavailable settings will not be included in the response object alert?: boolean; badge?: boolean; sound?: boolean; lockScreen?: boolean; carPlay?: boolean; notificationCenter?: boolean; criticalAlert?: boolean; } function checkNotifications(): Promise<{ status: PermissionStatus; settings: NotificationSettings; }>; ``` ```js import {checkNotifications} from 'react-native-permissions'; checkNotifications().then(({status, settings}) => { // … }); ``` --- #### requestNotifications Request notifications permission status and get notifications settings values. ```ts // only used on iOS type NotificationOption = | 'alert' | 'badge' | 'sound' | 'criticalAlert' | 'carPlay' | 'provisional'; interface NotificationSettings { // properties only availables on iOS // unavailable settings will not be included in the response object alert?: boolean; badge?: boolean; sound?: boolean; lockScreen?: boolean; carPlay?: boolean; notificationCenter?: boolean; criticalAlert?: boolean; } function requestNotifications( options: NotificationOption[], ): Promise<{ status: PermissionStatus; settings: NotificationSettings; }>; ``` ```js import {requestNotifications} from 'react-native-permissions'; requestNotifications(['alert', 'sound']).then(({status, settings}) => { // … }); ``` --- #### openSettings Open application settings. ```ts function openSettings(): Promise; ``` ```js import {openSettings} from 'react-native-permissions'; openSettings().catch(() => console.warn('cannot open settings')); ``` ## Additional recipes #### Check multiples permissions ```js import {check, PERMISSIONS} from 'react-native-permissions'; // can be done in parallel Promise.all([ check(PERMISSIONS.IOS.CAMERA), check(PERMISSIONS.IOS.CONTACTS), // … ]).then(([cameraStatus, contactsStatus /* … */]) => { console.log({cameraStatus, contactsStatus}); }); ``` #### Request multiples permissions _⚠️  It's a very bad UX pattern, avoid doing it!_ ```js import {request, PERMISSIONS} from 'react-native-permissions'; // should be done in sequence async function requestAll() { const cameraStatus = await request(PERMISSIONS.IOS.CAMERA); const contactsStatus = await request(PERMISSIONS.IOS.CONTACTS); return {cameraStatus, contactsStatus}; } console.log(requestAll()); ```