# ☝🏼 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.59.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 NSSiriUsageDescription YOUR TEXT ``` #### ⚠️ If you encounter the error `Invalid RNPermission X. Should be one of: ()` 1. Check that you linked **at least one** permission handler. 2. Clean up Xcode stale data with `npx react-native-clean-project --remove-iOS-build --remove-iOS-pods` 3. If you use `use_frameworks!`, replace it by `use_modular_headers!` - see [this blog post](http://blog.cocoapods.org/CocoaPods-1.5.0) for more details 4. If you use `use_frameworks!` but **can't** replace it with `use_modular_headers!`, check the following workaround: ```ruby # Add this code at the top of Podfile right after platform definition. # It will make all the dynamic frameworks turning into static libraries. use_frameworks! $dynamic_frameworks = ['RxCocoa', 'RxSwift', 'WhatEverSDKName'] pre_install do |installer| installer.pod_targets.each do |pod| if !$dynamic_frameworks.include?(pod.name) puts "Overriding the static_framework? method for #{pod.name}" def pod.build_type; Pod::Target::BuildType.static_library end end end end ``` ### Android Add all wanted permissions to your app `android/app/src/main/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 accept 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 accept β”‚ the requestΒ ? β”‚ β”‚ ╔════╗ Does the user check β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•‘ 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')); ``` ## Migrating from v1.x.x If you are currently using the version `1.x.x` and would like to switch to `v2.x.x`, the only thing you really need to know is that it's now required to select the wanted permission **per platform**. ```js // v1.x.x import Permissions from 'react-native-permissions'; Permissions.request('location', {type: 'whenInUse'}); // v2.x.x import {Platform} from 'react-native'; import {PERMISSIONS, request} from 'react-native-permissions'; request( Platform.select({ android: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION, ios: PERMISSIONS.IOS.LOCATION_WHEN_IN_USE, }), ); ``` ## 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}; } requestAll().then(statuses => console.log(statuses)); ```