iOS13 & iOS12で位置情報を「常に許可」してもらうための導線設計に関するあれこれ

※ 実装/動作確認環境: Xcode 11 beta7 + iOS 13 beta 8

## TL; DR
- iOS13 & iOS12で位置情報を「常に許可」してもらう場合、Info.plistの値を以下のようにすることで適切に説明・誘導できそう
  - `NSLocationWhenInUseUsageDescription`
      - XXXの利用には、「Appの使用中は許可」を選択してください。
  - `NSLocationAlwaysAndWhenInUseUsageDescription`
      - XXXの利用には、「常に許可」を選択してください。

## 背景と概要
- iOS13からは `CLLocationManager.requestAlwaysAuthorization()` を実行しても、**「常に許可」**をすぐに取得できないようになり、アラートで２度許可を取得する必要がある
  - 詳細はWWDCの発表内容[^1]や解説記事[^2] [^3]を参照
- iOS12もサポートするアプリにおいて、位置情報を「常に許可」してもらうために、２つのアラート(および設定アプリ内)で表示される位置情報の用途について、どのように設定するのが妥当か調査・検討した

## （おさらい）位置情報の用途の明示
位置情報を利用するためには、Info.plistに以下のKeyを設定し、用途を明示する必要がある

- [NSLocationWhenInUseUsageDescription](https://developer.apple.com/documentation/bundleresources/information_property_list/nslocationwheninuseusagedescription)
- [NSLocationAlwaysAndWhenInUseUsageDescription](https://developer.apple.com/documentation/bundleresources/information_property_list/nslocationalwaysandwheninuseusagedescription)
  - 位置情報の利用を「常に許可」してもらう場合のみ必要

なお、以下の説明では、簡単のため`NSLocationAlwaysAndWhenInUseUsageDescription`を**Always**、`NSLocationWhenInUseUsageDescription`を**WhenInUse**と表記する

## 表示される値の調査
何らかの方法で２度の確認アラートを回避する方法を模索した(ページ下部参照)が、結局は素直に２度のアラートでの許可取得に乗っかるしかなさそうと判断。
試してみたところ、呼び出す[CLLocationManager](https://developer.apple.com/documentation/corelocation/cllocationmanager)のメソッドやInfo.plistの定義内容によって、表示される値が異なるようなので、調査・整理した。

### `requestWhenInUseAuthorization()`の実行で表示される値
#### iOS12 & iOS13(共通)
|表示箇所|表示される値|
|:--|:--|
|許可取得アラート|WhenInUse|
|設定アプリ内※1|下記参照|

##### 設定アプリ内で表示される値
Info.plistの定義内容に応じ、表示される値は異なる

1. Info.plistに `WhenInUse` のみが定義されている場合
  - `WhenInUse` の値が表示される
1. Info.plistに `Always` と `WhenInUse` の両方が定義されている場合
  - `Always` の値が表示される
      - 位置情報の利用も「常に許可」が選択可能[^4]

### `requestAlwaysAuthorization()`の実行で表示される値
#### iOS12
|表示箇所|表示される値|
|:--|:--|
|許可取得アラート|Always|
|設定アプリ内※1|Always|

#### iOS13
|表示箇所|表示される値|
|:--|:--|
|許可取得アラート(１回目)※2|WhenInUse|
|許可取得アラート(２回目)※3|Always|
|設定アプリ内※1|Always|

- ※1: 設定 > アプリ名 > 位置情報 のフッターに表示されるもの
- ※2: {Appの使用中は許可, １度だけ許可, 許可しない}の３つの選択肢があるアラート
- ※3: {"使用中のみ許可"のままにする, "常に許可"に変更}の2つの選択肢があるアラート

## iOS13 & iOS12で「常に許可」に誘導するための文言設計

上記の調査内容に基づき、以下のような内容にする予定

- `WhenInUse`
  - XXXの利用には、「Appの使用中は許可」を選択してください。
- `AlWays`
  - XXXの利用には、「常に許可」を選択してください。

### このようにしたときの挙動
※`requestAlwaysAuthorization()`のみ利用する想定

- iOS12
  1. `requestAlwaysAuthorization()` のアラートで"「常に許可」を選択…"（=Alwaysの内容）が表示される
- iOS13
  1. `requestAlwaysAuthorization()` のアラートで"「Appの使用中は許可」を選択…"（=WhenInUseの内容）が表示される
  2. のちに表示されるアラートで"「常に許可」を選択…"（=Alwaysの内容）が表示される

## （参考）設定アプリ内の許可状態と取得できるステータスの対応

当初は「常に許可」でなければ設定アプリに誘導する手を検討した。
しかし、WWDC発表内容[^1]等にもある通り、詳細なステータス（WWDC資料では`Temporary`や`Provisional`と示されているもの）はラップされており、かつ調べてみると想像以上に扱いづらそうなので断念した。

設定アプリ内で参照できる許可状態とアプリ上で取得できる許可ステータスの対応を確認した結果は以下の通り。

|設定アプリ内の許可状態※1※2|取得できるステータス※3|
|:--|:--|
|(許可取得前)|notDetermined|
|なし|denied|
|次回確認（起動中に位置情報の許可取得を行っていない）|notDetermined|
|次回確認(起動中に「１回だけ許可」を選択)|authorizedWhenInUse|
|このAppの使用中のみ許可|authorizedAlways|
|常に|authorizedAlways|

- ※1 表内の文言はiOS13のもの(iOS12とは若干異なる)
- ※2 ()内の値は本表作成にあたり付記した注釈
- ※3 `CLLocationManager.authorizationStatus()`の値

`authorizedAlways`のステータスが「このAppの…」と「常に」に対応するのは、発表内容の通りであり、またプライバシー保護の旗印のもとであればやむなしといった印象。
が、`次回確認（起動中に位置情報の許可取得を行っていない）` と `次回確認(起動中に「１回だけ許可」を選択)` の状態があり、それぞれ対応するステータスとの入り組み具合を見ると、(自分で書いたものながら)「お前は何を言っているんだ」感がすごい。


[^1]: [What's New in Core Location](https://developer.apple.com/videos/play/wwdc2019/705/)
[^2]: [iOS 13ではCLLocationManagerでrequestAlwaysAuthorizationを呼んでも常に許可という選択肢はでない](https://qiita.com/YujiHamada3/items/3d750c336ecfb51677d0)
[^3]: [iOS13から位置情報の権限周りの挙動が変わるかも](https://qiita.com/owen/items/451a17a14b01687ee0ec)
[^4]: どの方法で許可を取得しようとしたかではなく、`NSLocationAlwaysAndWhenInUseUsageDescription`の有無で決まる模様。[ドキュメント](https://developer.apple.com/documentation/bundleresources/information_property_list/nslocationalwaysandwheninuseusagedescription)にも、「この値は位置情報へのアクセスを常に行う際に必要」と口酸っぱく念押しのように書かれている。