CarPlay Navigation Appのナビ情報の扱い方

CarPlayでナビゲーション機能を実装する時の実装についてです。
Appleのガイドを読むとプロジェクト設定などはわかるのですが、実際にナビ情報を表示しようと思うとよくわからなかったのでまとめておきます。

## 参考資料

Appleのナビゲーションアプリのガイド

https://developer.apple.com/carplay/documentation/CarPlay-Navigation-App-Programming-Guide.pdf

Appleのサンプルコードを参考にしました。

https://developer.apple.com/documentation/carplay/integrating_carplay_with_your_navigation_app/

## ルート検索結果の表示
ルート検索を何かしらの方法で実行し、その結果をユーザーに示します。

![スクリーンショット 2023-10-29 17.07.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570572/aeb82e89-1fa5-f39c-8337-3b9c6fe2d9d4.png)


CPMapTemplate.showTripPreviewsでルート検索結果のプレビューを表示します。

https://developer.apple.com/documentation/carplay/cpmaptemplate/3014006-showtrippreviews

渡すのはCPTripです。これが一つのルートを表現します。
CPTripに複数のCPRouteChoiceを持たせることでルートの選択肢を表現します。

![スクリーンショット 2023-10-29 17.08.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570572/0378a1a3-54de-6e26-6a0a-64d08262ca72.png)

### ルート検索結果の到着予想時刻の表現
CPTravelEstimatesを使用します。距離と時間を設定できます。設定するとオレンジの枠線内の表示が行われます。
![スクリーンショット 2023-10-29 17.07.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570572/c23d1786-935c-10d5-72e5-ac804ad65b54.png)


https://developer.apple.com/documentation/carplay/cptravelestimates

CPMapTemplate.updateEstimatesに設定することで、Previewでルートの所要時間や距離を表示できます。

#### ルート検索結果が複数あるの到着予想時刻の切り替え
CPRouteChoiceのuserInfoにestimatesを設定しておくことで表示を切り替えることができます。

下記のDelegateメソッドでプレビューされるルート選択時の通知を受け取ることができます。
```swift
func mapTemplate(_ mapTemplate: CPMapTemplate, selectedPreviewFor trip: CPTrip, using routeChoice: CPRouteChoice)
```

このタイミングで、CPRouteChoice.userInfoから先ほど設定したestimatesを取得し、CPMapTemplate.updateEstimatesに渡すことで表示を更新できました。

## ナビ開始
CPMapTemlateのstartNavigationSessionを使用することでナビを開始します。戻り値のCPNavigationSessionを扱うことでナビの状態を管理します。

https://developer.apple.com/documentation/carplay/cpmaptemplate/2968364-startnavigationsession

## ナビ開始後にナビ情報を更新する

ナビゲーション情報はCPManeuverを使って表現します。

https://developer.apple.com/documentation/carplay/cpmaneuver

CPNavigationSession.upcomingManeuversプロパティに次の曲がる場所などのナビ情報(CPManeuver)を設定します。すると下記の図のように曲がる場所までの距離と説明が表示できます。upcomingManeuversの値を更新するとアニメーション付きで表示が切り替わります。
![スクリーンショット 2023-10-29 17.24.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570572/4fbc8ae6-4a52-f47f-2330-9decfd86934b.png)

そして、CPNavigationSession.updateEstimates(_:for:)メソッドで、上記で設定したナビ情報(CPManeuver)の更新を行います。アニメーションは行われません。これにより、次の曲がる場所までの距離の文字列のみを更新することができます。

https://developer.apple.com/documentation/carplay/cpnavigationsession/2968406-updateestimates

下記はサンプルコードのからの一部改変です。
```swift
// ナビ開始
let session = mapTemplate.startNavigationSession(for: trip)
// 次の曲がる場所までの情報をCPManeuverで設定して、sessionに設定する。自動的にCPMapTemplateの表示が行われる。
session.upcomingManeuvers = [maneuver]

// 曲がる場所までの距離を1000mとする
let distance = 1000
repeat {
	let newDistance = Measurement(value: distance, unit: UnitLength.meters)
    let estimates = CPTravelEstimates(distanceRemaining: newDistance as Measurement<UnitLength>, timeRemaining: 0)
    session.updateEstimates(estimates, for: maneuver)
	distance -= 5
	sleep(1)
} while (distance >= 0)

// 距離の更新が終了したら、ルート全体の所要時間を更新
self.baseMapTemplate?.updateEstimates(newEstimates, for: session.trip)
```


## サンプルコードがナビ開始でクラッシュする問題
サンプルコードをXcode15のiOSシミュレータとExternal DisplayでCarPlayを表示した場合に、ナビ開始ができませんでした。
クラッシュログを調査すると、Instrument Clusterの部分で問題が発生していたようです。これは車のダッシュボードにCarPlayの表示をすることができる機能です。
info.plistのApplication Scene ManifestでInstrumentClusterの設定をFalseに変更したらナビ開始できるようになりました。