iOS14以降のPHAuthorizationStatus

# はじめに
iOSでは、カメラロールにアクセスするためには、ユーザに対して適切に権限をリクエストする必要があります。
iOS14になり、PHAccessLevel や PHAuthorizationStatus.limited が追加され、挙動が複雑になったので、調べたことの備忘録です。
「写真のアクセスを許可」の状態に応じた PHAuthorizationStatus と、できることのまとめです。

# iOS13以下の場合
* 確認したOSバージョン： iOS13.4.1

|「写真のアクセスを許可」  |authorizationStatus()  |requestAuthorization()  |
|---|:-:|:-:|
|未確認(初期状態)  |.notDetermined  |○ （表示される）  |
|読み出し/書き込み  |.authorized  |× （表示されない）  |
|許可しない  |.denied  |×  |

* とてもシンプルです


# iOS14以降の場合
確認したOSバージョン： iOS14.2.1

## 未確認(初期)の状態
|「写真のアクセスを許可」  |authorizationStatus()  |authorizationStatus<br>(for: .addOnly)  |authorizationStatus<br>(for: .readWrite)  |requestAuthorization<br>(for: .addOnly)  |requestAuthorization<br>(for: .readWrite)  |
|---|:-:|:-:|:-:|:-:|:-:|
|未確認  |.notDetermined  |.notDetermined  |.notDetermined  |○  |○  |

## requestAuthorization(for: .addOnly) のみ確認
* 設定画面の状態

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/6afb8767-6331-a58f-56cd-15d0a5a7a33e.jpeg" width="400">

* authorizationStatusの結果

|「写真のアクセスを許可」| authorizationStatus() | authorizationStatus<br>(for: .addOnly) | authorizationStatus<br>(for: .readWrite)  |requestAuthorization<br>(for: .addOnly) | requestAuthorization<br>(for: .readWrite) |
|:-:|:-:|:-:|:-:|:-:|:-:|
|写真の追加のみ<br>(requestAuthorization(for: .addOnly)で「OK」)  |.notDetermined  |.authorized  |.notDetermined  | ×  | ○  |
|なし<br>(requestAuthorization(for: .addOnly)で「許可しない」)  |.notDetermined  |.denied  |.notDetermined  |×  |○  |

## requestAuthorization(for: .readWrite) のみ確認
* 設定画面の状態

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/6ef17ab3-8267-6d29-afb1-b32dcb64760e.jpeg" width="400">

* authorizationStatusの結果

|「写真のアクセスを許可」  |authorizationStatus()  |authorizationStatus<br>(for: .addOnly)  |authorizationStatus<br>(for: .readWrite)  |requestAuthorization<br>(for: .addOnly)  |requestAuthorization<br>(for: .readWrite)  |
|---|:-:|:-:|:-:|:-:|:-:|
|選択した写真<br>(requestAuthorization(for: .readWrite)で「選択した写真」)  |.authorized  |.limited  |.limited  |×  |×  |
|すべての写真<br>(requestAuthorization(for: .readWrite)で「全ての写真へのアクセスを許可」)  |.authorized  |.authorized  |.authorized  |×  |×  |
|なし<br>(requestAuthorization(for: .readWrite)で「許可しない」)  |.denied  |.notDetermined  |.denied  |○  |×  |


## requestAuthorization(for: .addOnly) と requestAuthorization(for: .readWrite) の両方を確認
* 再現手順
authorizationStatus(for: .addOnly) と authorizationStatus(for: .readWrite) のどちらか片方だけが .notDetermined の時に、.notDetermined の方のAccessLevel で requestAuthorization する


* 設定画面の状態

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214313/cc23b757-864e-8e76-815b-443218d83143.jpeg" width="400">

* authorizationStatusの結果

|「写真のアクセスを許可」  |authorizationStatus()  |authorizationStatus<br>(for: .addOnly)  |authorizationStatus<br>(for: .readWrite)  |
|---|:-:|:-:|:-:|
|写真の追加のみ|.denied  |.authorized  |.denied  |
|選択した写真  |.authorized  |. limited  |. limited  |
|すべての写真 |.authorized  |.authorized  |.authorized  |
|なし |.denied  |.denied  |.denied  |

# iOS14以降にできるだけアルバムに写真を保存する
## やりたいこと
1. アルバムを指定して保存できる時は、アルバムを指定して保存する
2. アルバムを指定して保存できない時は、カメラロールに保存する
3. カメラロールにも画像を保存できない時は、何もしない

### アルバムを指定して保存できる時は、アルバムを指定して保存する
readWrite の AuthorizationStatus が .authorized の時に保存できる

> PHPhotoLibrary.authorizationStatus(for: .readWrite) == .authorized

### アルバムを指定して保存できない時は、カメラロールに保存する
addOnly の AuthorizationStatus が .authorized もしくは .limited の時に保存できる

> PHPhotoLibrary.authorizationStatus(for: . addOnly) == .authorized or . limited

## 判定器

```swift
class AuthorizationStatusChecker {
    enum PhotoLibraryAuthorizationStatus {
        case readWrite
        case addOnly
        case denied
    }
    static func checkPhotoLibrary(handler: @escaping (PhotoLibraryAuthorizationStatus) -> Void) {
        let readWrite = PHPhotoLibrary.authorizationStatus(for: .readWrite)
        let addOnly = PHPhotoLibrary.authorizationStatus(for: .addOnly)
        switch (readWrite, addOnly) {
        case (.authorized, _):
            handler(.readWrite)
        case (.notDetermined, _):
            PHPhotoLibrary.requestAuthorization(for: .readWrite) { (_: PHAuthorizationStatus) in
                Self.checkPhotoLibrary(handler: handler)
            }
        case (_, .authorized), (_, .limited):
            handler(.addOnly)
        case (_, .notDetermined):
            PHPhotoLibrary.requestAuthorization(for: .addOnly) { (_: PHAuthorizationStatus) in
                Self.checkPhotoLibrary(handler: handler)
            }
        default:
            handler(.denied)
        }
    }
}
```

## 使い方
```swift
AuthorizationStatusChecker.checkPhotoLibrary { [weak self] (status) in
    switch status {
    case .readWrite:
        self?.saveToAlbum(name: "MyAlbum")
    case .addOnly:
        self?.saveToCameraRoll()
    case .denied:
        break
    }
}
```