[iOS14]WWDC 2020 Core Location 新要素 Preciseについて

## [What's new in location](https://developer.apple.com/videos/play/wwdc2020/10660/)

### 要点

iOS14以降は、
・位置情報精度について、`full`、`reduced`の２パターンが追加され、どちらの値だったとしてもアプリ側が対応できるようにしておく必要がある。
・`full`精度ではないとできないような機能がある場合、「一時的な`full`精度の許可」を求めるか、「設定アプリにユーザーを飛ばして設定を変更してもらう」という対応のどちらかにすると良い。
・必要もないのに`full`精度の許可をユーザーに求めるような事は適切でない。`reduced`精度をデフォルト設定とする事もできるので、選択肢の一つとして考えるべき。

### 詳細

iOS13以降では、位置情報取得の可否についてユーザーは許諾ダイアログまたは設定アプリから３通りの選択肢を選ぶことができた。

- **Never**(取得不可)/ **While Using**(アプリ使用中のみ取得可)/ **Always**(常に取得可)

※ Always(常に取得可)は最初の許諾ダイアログでいきなり選択することができるわけではなく、「Once(一回許可)」を押した後、少し時間が経ってから常に取得可能に変更するかどうか尋ねるダイアログが出現する形式であった。

→ アプリの種類によっては、非常に正確な位置情報がいるもの（地図アプリなど）と、位置情報の精度はユーザーの好みに任せていいもの（SNS、マッチングアプリなど）がある。つまり取得する情報の精度をコントロールできた方がいい。そのため、iOS14以降から、**Precise(精度)**という項目が位置情報許諾ダイアログ並びに設定アプリに追加され、正確な位置情報まで送るかどうかを選べるようになった。（**Full（正確な位置情報）**か、**Reduced(大まかな位置情報)**の二択）

精度：fullの場合
<img width="206" alt="截圖 2020-07-12 11.05.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392332/251fe6a1-4085-5f5a-48f2-3c8c6233ea4c.png">

精度：reducedの場合
<img width="208" alt="截圖 2020-07-12 12.30.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392332/743b10a2-ac30-1f3f-41bb-037c52112edb.png">


※見ればわかるように、精度のほかユーザーがいる地点周辺の地図も一緒に表示されるようになった。

これに合わせて、`CLLocationManager`クラスの[authorizationStatus](https://developer.apple.com/documentation/corelocation/cllocationmanager/1423523-authorizationstatus)メソッドは廃止予定になり、以下の列挙形で位置情報精度を表すようにする。

[CLAccuracyAuthorizaion](https://developer.apple.com/documentation/corelocation/claccuracyauthorization)

```swift: 
var accuracyAuthorization: CLAccuracyAuthorization { get }

enum CLLAccuracyAuthorization {
   case fullAccuracy
   // 正確な精度で位置情報を提供する事をユーザーが許可した場合。iOS13以前はこれしかなかったのと同じ状態。

   case reducedAccuracy
   // 大まかな精度でのみ位置情報を提供する事をユーザーが許可した場合。
}
```

また[`CLAuthorizationStatus`](https://developer.apple.com/documentation/corelocation/clauthorizationstatus)か[`CLAccuracyAuthorization`](https://developer.apple.com/documentation/corelocation/claccuracyauthorization)の**どちらか一方でも**変更があった場合、以下の新規追加されたデリゲートメソッドが呼ばれるようになった。

```swift:
locationManager(_:didChangeAuthorization:) //deprecated

locationManagerDidChangeAuthorization(_:) //新規追加(iOS14以降)

//使用例

func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
 switch manager.authorizationStatus {
   case .authorizedAlways, .authorizedWhenInUse:
      //フラグON
      self.hasSomePermission = true
   case .notDetermined, .denied, .restricted:
      //フラグOFF
      self.hasSomePermission = false
   default: 
      print("Unhandled case")
 }

 switch manager.accuracyAuthorization {
    case .reducedAccuracy: 
       self.gettingExactLocation = false
    case .fullAccuracy:
       self.gettingExactLocation = true
   default: 
      print("This should not happen!")
 }

}
```

-> **従来位置情報を使用していたアプリについて、`accuracyAuthorization`がどちらの値になっても対応できるようにする必要がある。**

- 位置情報は従来通り、[`locationManager:didUpdateLocations:`](https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/1423615-locationmanager?language=objc)メソッドで`CLLocation`クラスオブジェクトとして渡されてくる。これは１時間に４回ほどアップデートされるのだが、精度が`fullAccuracy`の場合、ユーザーのいる代表的な座標と考えるのではなく、ユーザーが本当にその時点でいる座標として取り扱うこと。

- AppleのMapアプリも`reducedAccuracy`をサポートする形でアップデートされる。具体的には、ユーザーの位置は青い点ではなく、大まかな位置を示す円で表されることになる。

- `full`の精度が必要なのに、`reduced`の精度の取得許可しかユーザーから得られていない場合どうするか。
   - 設定アプリへ飛ばし、設定を変更するようユーザーに促す。ただユーザーにとって面倒な動作なので、あまりやりたくはない。 
   - 新しいメソッドの[`requestTemporaryFullAccuracyAuthorization(withPurposeKey:completion:)`](https://developer.apple.com/documentation/corelocation/cllocationmanager/3600217-requesttemporaryfullaccuracyauth)を使い、一時的に`full`精度の位置情報取得を行う許可を得ることが可能。この後は、位置情報そのものの許可ダイアログで「Once(一回許可)」を押した時と似た振る舞いとなり、次にアプリを起動した際、`full`精度の位置情報取得を恒久的に許可するかのダイアログがユーザーに表示されることとなる。

```swift: 
@IBAction func userWantsToNavigate(_ sender: Any) {
   
   //`reduced`の精度の取得許可しかユーザーから得られていない
   if manager.accuracyAuthorization = .reducedAccuracy {

      //一時的な許可をユーザーに求める
　　　 //puroposeKeyとは、InfoPlistの NSLocationTemporaryUsageDescriptionDictionaryの中にあらかじめ記載した、キー文字列。
      manager.requestTemporaryFullAccuracyAuthorization(withPurposeKey: "WantsToNavigate") {
         
          //許可が得られた場合
          if self.manager.accuracyAuthorization = .fullAccuracy {
             self.beginNavigation()
          } 
      }

   } else {
      self.beginNavigation()

   }
}
```

```:Info.plist
<key>NSLocationTemporaryUsageDescriptionDictionary</key>
<dict>
   <key>WantsToNavigate</key>
   <string>歩行ルートを計算するために正確な位置情報が必要です。</string>
   <key>HogeHoge</key>
   <string>HogeHogeするために正確な位置情報が必要です。</string>
</dict>
```

- 方針
   - 精度が`reduced`の場合、AppleのMapアプリのように、それをUIに反映させることが推奨される。例えば、精度が`reduced`の場合はユーザーの大まかな位置を円で表す・現在の精度が何かをラベルで表示するなど。これにより、`reduced`でもアプリが対応できるようにし、必要もないのに常に`full`精度を要求する事は止める。
   - 精度が`full`でないと使えない機能がある場合（Mapアプリでいうルート検索機能など）は、上で書いたように一時許可を求めても問題ない。Mapアプリは、このような一時許可を求める形式を取っている。

- [Significant-change location services](https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/using_the_significant-change_location_service)

精度が`reduced`が適用され、低い精度にしても多くの距離を移動したと判定できる場合のみ通知される。同じ場所に止まっていると判定できる場合は、従来通り通知しない。

- [Visits monitoring API](https://developer.apple.com/documentation/corelocation/getting_the_user_s_location/using_the_visits_location_service)

やはり精度`reduced`が適用される。通知されるタイミングについては`full`でも変わらないが、通知される場所が大まかなものになる（例えば、`full`なら「◯○公園に到着」と通知される所、`rduced`では「××市に到着」となる等）。

- [Beacon](https://developer.apple.com/documentation/corelocation/ranging_for_beacons)および[Geographical Region Monitoring](https://developer.apple.com/documentation/corelocation/monitoring_the_user_s_proximity_to_geographic_regions)

精度`reduced`では**使用できない** = 常に`full`精度の位置情報が必要。
例として、Apple公式の`Reminder`アプリでは、特定のエリアを起点にしてリマインドを行う機能がある。これは`Region Monitoring`により行われている。

`Reminder`アプリのこの機能では、常に`full`精度の位置情報を要求し、`reduced`精度では意味をなす動作ができない。このことから、上に述べた`Map`アプリのような、一時的に`full`精度を求めるような事はせず、端的にユーザーに設定アプリに飛んで`full`精度を許可するよう促す形式を取っている。

このように、設定アプリに飛んで設定変更を促すという動作を自作アプリでしたい時は、果たして適切かどうかを考える必要がある。

- `full`精度の許可が得られているものの、実際には`reduced`精度しか必要がない場合
   - このような場合は、`LocationManager`クラスの`desiredAccuracy`に、`kCLLocationAccuracyReduced`というiOS14からの新しい値をセットする。

```swift:
var locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyReduced
```

- そもそも`reduced`精度しか許可を求めない場合

```:Info.plist
<key>NSLocationDefaultAccuracyReduced</key>
<true/>
```
 `Info.plist`に上記のように記載すると、`Reduced`精度のみ許可を求めるようになる（精度を選ぶボタンが表示されなくなる）。

<img width="323" alt="截圖 2020-07-18 15.06.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392332/ff0da295-1d57-8888-210a-3d014b964ebf.png">

ただ、この設定をしても`Settings`アプリから`full`精度を選択する事はなお可能となっている。

<img width="203" alt="截圖 2020-07-18 15.08.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392332/647f8d2f-65b6-4aca-f568-d65a6260cb6c.png">

Apple公式の `TV`アプリでは、視聴できる番組を制限するためユーザーの地域を知る必要があり、位置情報を取得している。ただどの国にいるかなど大まかな位置だけ知れれば良いために、この機能を利用している。

- `reduced`精度の裏側の実装

`reduced`精度は、「ユーザーの本当の位置の周りにノイズを付加する」というような方法で算出されるのではない。地面をあるエリアごとに分け、１時間におよそ４回ほど、ユーザーがどのエリアにいるかを計測して行っている。１エリアの大きさは都市地域では数キロごとだが、人口がまばらな地域では10キロを超えることがある。このように大まかなエリアごとの区分けでしかないため、実際にいる市の隣の市にいると判定されることもありうる。

- Watch Appとの連携

Apple Watchのコンパニオンアプリ内で制度に関する許可をした場合、これと連動するiPhoneアプリでも自動的に許可が適用される。

参考

[What's new in location](https://developer.apple.com/videos/play/wwdc2020/10660/)