koogawa blog

iOS、Android、foursquareに関する話題

CarPlay対応アプリを雰囲気で作ってみる

これはなに

CarPlay対応アプリを開発する際の手順や、「できること/できないこと」をなんとなく理解するために、 とりあえず動くCarPlay対応のAudioアプリを作ってみたときの雑なメモです。

開発環境

注意事項

実機(車載ナビ)でテストする場合は、下記URLからアップルへ連絡が必要になります。

https://developer.apple.com//contact/carplay/

1ヶ月ほど待つと、アップルから CarPlay Audio App Programming Guide がメールで送られてきます。このタイミングでデベロッパーアカウントにCarPlay entitlementがアサインされ、CarPlay対応アプリを実機で動かすことが許可されます。

f:id:koogawa:20190115001824p:plain

なお、Simulatorでのテストだけであれば上記の連絡なしでもできるようです。

勘違いしていたこと

  • CarPlay対応アプリは Watch App 等のようにターゲットを分けて作るわけではなかった
  • AppDelegate を拡張して CarPlayでも動くようにするイメージ
  • なので CarPlay 専用のアプリストアも存在しない

開発手順

1. Include the CarPlay audio app entitlement

Entitlements.plistcom.apple.developer.playable-content を追加します。

f:id:koogawa:20190115001912p:plain

これを追加するだけでCarPlayのホーム画面に自分のアプリが表示されるようになります。

2. Show an app icon on the CarPlay home screen

CarPlay用のアプリアイコンをAssetsにセットします。

f:id:koogawa:20190115001927p:plain

3. Extend AppDelegate

AppDelegate を拡張し、CarPlayに表示するビューコントローラをセットします。

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    var carWindow: UIWindow?

    func updateCarWindow() {
        guard let screen = UIScreen.screens.first(where: {$0.traitCollection.userInterfaceIdiom == .carPlay}) else {
            self.carWindow = nil
            return
        }

        // CarPlay is connected
        let carWindow = UIWindow(frame: screen.bounds)
        carWindow.screen = screen
        carWindow.makeKeyAndVisible()
        carWindow.rootViewController = CarViewController()
        self.carWindow = carWindow
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        updateCarWindow()
    }

〜

CarPlay用の UIWindow を別途用意し、その rootViewController にビューコントローラをセットするイメージですね。

4. Present a hierarchical list to navigate and select audio content

曲リストを作るために MPPlayableContentDataSourceMPPlayableContentDelegate プロトコルを実装します。名前からわかるように前者はリストに表示するデータをセットし、後者は曲が選択された際のアクションを定義します。

class CarViewController: UIViewController, MPPlayableContentDataSource, MPPlayableContentDelegate {
    func numberOfChildItems(at indexPath: IndexPath) -> Int {
        return 3
    }

    func contentItem(at indexPath: IndexPath) -> MPContentItem? {
        let item = MPContentItem.init(identifier: UUID.init().uuidString)
        item.title = "hoge rock"
        item.subtitle = "huga"
        item.isContainer = false
        item.isPlayable = true
        item.artwork = MPMediaItemArtwork(image: UIImage(named: "koogawa")!)
        return item
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        MPPlayableContentManager.shared().dataSource = self
        MPPlayableContentManager.shared().delegate = self

    }

    func playableContentManager(_ contentManager: MPPlayableContentManager,
                                initiatePlaybackOfContentItemAt indexPath: IndexPath,
                                completionHandler: @escaping (Error?) -> Void) {
        // 曲が選択された!
        completionHandler(nil)
    }

今回は固定データを表示するだけですが、実際には動的にコンテンツを取得し、曲が選択された際にはネットワーク上の曲をストリーミング再生する、などの実装が必要になります。

実行方法

ここでいったんアプリを実行してみます。

f:id:koogawa:20190115002158p:plain

iOS Simulatorのメニューから Hardware > External Displays > CarPlayCarPlay用のSimulatorが起動します。

f:id:koogawa:20190115002217p:plain

曲リストが表示されましたね!

がっつり実装したい場合は

今回、自分が実装したのはここまでですが、完全なオーディオアプリを開発する場合は次の実装も必要になります。

  • 再生中の曲情報を表示する Now Playing screen に情報を提供する
  • リモコンによるイベント(再生・停止・次の曲など)ハンドリング

詳細はアップルから送られてくるCarPlay Audio App Programming Guideを参照してください。

また、偶然GITHUB上で見つけた下記リポジトリも大変参考になりました。

資料