react-native-permmisionsを使用したパーミッションリクエストについて

## 環境

ReactNative 0.63.4
react-native-permissions 3.0.0

## インストール

```sh
$ npm i react-native-permissions
$ cd ios
$ pod install
```

## iOS設定

Podfileに追加。今回はカメラを追加します。

```:ios/Podfile
  permissions_path = '../node_modules/react-native-permissions/ios'
    pod 'Permission-Camera', :path => "#{permissions_path}/Camera"
```


Info.plistに必要なパーミッションの説明文を追加します。

```plist:ios/project/Info.plist
	<key>NSCameraUsageDescription</key>
    <string>カメラへのアクセスを許可することでこのアプリの機能を使用することができます。</string>
```

## Android設定

```xml:android/app/src/main/AndroidManifest.xml
  <uses-permission android:name="android.permission.CAMERA" />
```

## パーミッションフローの理解

[下記フローチャートは公式より](https://github.com/zoontek/react-native-permissions/blob/master/README.md)
checkメソッドでアプリのパーミッションステータスを確認して返り値に応じてrequestメソッドでリクエストするという流れになります。

### iOS flow

iOSは一度ユーザに「許可しない」を選択されるとリクエストすることができません。

```
   ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
   ┃ check(PERMISSIONS.IOS.CAMERA) ┃
   ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
                   │
       デバイスで機能が利用できるか？
                   │           ╔════╗
                   ├───────────║ NO ║──────────────┐
                   │           ╚════╝              │
                ╔═════╗                            ▼
                ║ YES ║                 ┌─────────────────────┐
                ╚═════╝                 │ RESULTS.UNAVAILABLE │
                   │                    └─────────────────────┘
       パーミッションリクエストが可能か？
                   │           ╔════╗
                   ├───────────║ NO ║──────────────┐
                   │           ╚════╝              │
                ╔═════╗                            ▼
                ║ YES ║                  ┌───────────────────┐
                ╚═════╝                  │ RESULTS.BLOCKED / │
                   │                     │ RESULTS.LIMITED / │
                   │                     │  RESULTS.GRANTED  │
                   ▼                     └───────────────────┘
          ┌────────────────┐
          │ RESULTS.DENIED │
          └────────────────┘
                   │
                   ▼
  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
  ┃ request(PERMISSIONS.IOS.CAMERA) ┃
  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
                   │
         ユーザがリクエストを許可したか？
                   │           ╔════╗
                   ├───────────║ NO ║──────────────┐
                   │           ╚════╝              │
                ╔═════╗                            ▼
                ║ YES ║                   ┌─────────────────┐
                ╚═════╝                   │ RESULTS.BLOCKED │
                   │                      └─────────────────┘
                   ▼
          ┌─────────────────┐
          │ RESULTS.GRANTED │
          └─────────────────┘
```

### Android flow

Androidは一度リクエストが拒否されても「今後は確認しない」にチェックを付けない限り何度でもリクエストすることができましたが、Android11以降はユーザが2回「許可しない」を選択した場合、パーミッションリクエストのダイアログを表示することができなくなりました。
[Android 11 での権限に関する更新](https://developer.android.com/about/versions/11/privacy/permissions?hl=ja)

```
 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
 ┃ check(PERMISSIONS.ANDROID.CAMERA) ┃
 ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
                   │
       デバイスで機能が利用できるか？
                   │           ╔════╗
                   ├───────────║ NO ║──────────────┐
                   │           ╚════╝              │
                ╔═════╗                            ▼
                ║ YES ║                 ┌─────────────────────┐
                ╚═════╝                 │ RESULTS.UNAVAILABLE │
                   │                    └─────────────────────┘
      パーミッションリクエストが可能か？
                   │           ╔════╗
                   ├───────────║ NO ║──────────────┐
                   │           ╚════╝              │
                ╔═════╗                            ▼
                ║ YES ║                  ┌───────────────────┐
                ╚═════╝                  │ RESULTS.BLOCKED / │
                   │                     │  RESULTS.GRANTED  │
                   ▼                     └───────────────────┘
          ┌────────────────┐
          │ RESULTS.DENIED │◀──────────────────────┐
          └────────────────┘                       │
                   │                               │
                   ▼                               │
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓         ╔════╗
┃ request(PERMISSIONS.ANDROID.CAMERA) ┃         ║ NO ║
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛         ╚════╝
                   │                               │
   ユーザがリクエストを許可したか？                     │
                   │                               │
                   │           ╔════╗              │
                   ├───────────║ NO ║─────「今後は確認しない」にチェックを付けたか？
                   │           ╚════╝              │
                ╔═════╗                         ╔═════╗
                ║ YES ║                         ║ YES ║
                ╚═════╝                         ╚═════╝
                   │                               │
                   ▼                               ▼
          ┌─────────────────┐             ┌─────────────────┐
          │ RESULTS.GRANTED │             │ RESULTS.BLOCKED │
          └─────────────────┘             └─────────────────┘
```

## 主なAPI

### check

パーミッション状態を確認する。

| 返り値 | 説明 |
|---|---|
|RESULTS.UNAVAILABLE| デバイスで使用不可|
|RESULTS.DENIED| パーミッションリクエストされていない/リクエストできるが拒否された|
|RESULTS.GRANTED |パーミッションが許可されている|
|RESULTS.LIMITED |パーミッションが許可されているが制限されている|
|RESULTS.BLOCKED |パーミッションが許可されていないかつリクエスト不可|

### request

パーミッションリクエストを送る。
第2引数のrationalはパーミッションが許可されず再度リクエストされた際になぜその権限が必要なのかユーザに説明するための表示として使用されます(Androidのみ)。
https://reactnative.dev/docs/permissionsandroid#request

### openSettings

アプリの設定画面を開く。パーミッションリクエストが許可されなかったときに、ユーザに設定画面から許可してもらうよう促す際に使用する。


## サンプル

```jsx:App.js
import React from 'react';
import {SafeAreaView, Button, Alert, Platform} from 'react-native';
import {
  check,
  openSettings,
  PERMISSIONS,
  request,
  RESULTS,
} from 'react-native-permissions';

const App: () => React$Node = () => {
  return (
    <SafeAreaView
      style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
      <Button
        title="request camera permission"
        onPress={async () => {
          const result =
            Platform.OS === 'ios' ? await requestIOS() : await requestAndroid();
          console.log(result);
        }}
      />
    </SafeAreaView>
  );
};

const promptForPermission = () => {
  Alert.alert(
    'カメラを使用する',
    '設定からカメラのアクセスを許可してください',
    [
      {
        text: '設定画面を開く',
        onPress: async () => await openSettings(),
      },
      {
        text: '閉じる',
      },
    ],
  );
};

const requestIOS = async () => {
  const cameraPermissionStatus = await check(PERMISSIONS.IOS.CAMERA);

  if (cameraPermissionStatus === RESULTS.UNAVAILABLE) {
    Alert.alert('デバイスのカメラを使用することが出来ません');
    return false;
  }

  // リクエスト不可
  if (cameraPermissionStatus === RESULTS.BLOCKED) {
    // アプリの機能を利用できるように設定から権限を変更するようユーザに促すダイアログを表示させる
    promptForPermission();
    return false;
  }

  // リクエスト可能
  if (cameraPermissionStatus === RESULTS.DENIED) {
    // リクエスト結果
    const cameraPermissionRequestResult = await request(PERMISSIONS.IOS.CAMERA);

    return cameraPermissionRequestResult === RESULTS.GRANTED;
  }

  return true;
};

const requestAndroid = async () => {
  const cameraPermissionStatus = await check(PERMISSIONS.ANDROID.CAMERA);

  if (cameraPermissionStatus === RESULTS.UNAVAILABLE) {
    Alert.alert('デバイスのカメラを使用することが出来ません');
    return false;
  }

  // リクエスト不可
  if (cameraPermissionStatus === RESULTS.BLOCKED) {
    promptForPermission();
    return false;
  }

  // リクエスト可能(初回パーミッションリクエスト)
  if (cameraPermissionStatus === RESULTS.DENIED) {
    const cameraPermissionRequestResult = await request(
      PERMISSIONS.ANDROID.CAMERA,
    );
    // 許可する
    if (cameraPermissionRequestResult === RESULTS.GRANTED) {
      return true;
    }
    // 「今後表示しない」にチェックをつけて許可しない
    if (cameraPermissionRequestResult === RESULTS.BLOCKED) {
      promptForPermission();
      return false;
    }
    // 許可しない
    return false;
  }

  return true;
};

export default App;
```

## 重要なこと

* パーミッションを必要最小限にする
* ユーザが必要とするまでパーミッションをリクエストしない
* なぜそのパーミッションが必要なのかリクエストする前にユーザに説明する


## 参考

[Request app permissions  |  Android Developers](https://developer.android.com/training/permissions/requesting)
[Privacy best practices  |  Android Developers](https://developer.android.com/privacy/best-practices)
[Requesting Permission - App Architecture - iOS - Human Interface Guidelines - Apple Developer](https://developer.apple.com/design/human-interface-guidelines/ios/app-architecture/requesting-permission/)