# ☝🏼 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 # if you have prebuild dynamic cocoapods dependencies and could not migrate # to use_modular_headers you should use workaround for linking app with dynamic frameworks # and with static libraries by placing this code at the top of Podfile # # Add this code at the top of Podfile right after platform definition use_frameworks! dynamic_frameworks = ['RxCocoa', 'RxSwift', 'WhatEverSDKName'] # make all the other dependencies into static libraries by overriding the static_library pre_install do |installer| installer.pod_targets.each do |pod| if !dynamic_frameworks.include?(pod.name) puts "Overriding the static_library for #{pod.name}" def pod.build_type; Pod::Target::BuildType.static_library # for static framework - # Pod::Target::BuildType.static_framework end end end end ``` ```ruby # 🚨 If you use use_framework! 🚨 # - Ensure that you have installed at least Cocoapods 1.5.0 # - Replace use_framework! with use_modular_headers! # (see http://blog.cocoapods.org/CocoaPods-1.5.0 for more details) 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 ``` _⚠️ If you encounter the error `Invalid RNPermission X. Should be one of: ()`, first check that you link at least one permission handler. If you did, try to cleanup Xcode junk data with `npx react-native-clean-project --remove-iOS-build --remove-iOS-pods`_ 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 ``` ### 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)); ```