CarPlay 対応アプリを作りたいと相談を受けるたびに、私はまず「どのカテゴリで申請しますか?」と聞きます。実はここが、多くの個人開発者が最初につまずくポイントなのです。Apple は CarPlay の利用を「音楽・オーディオ」「ナビゲーション」「EV 充電」「駐車場」「食事注文」「メッセージング」「VoIP 通話」「運転タスク」といった限定カテゴリにしか認めていません。どのカテゴリで申請するかを間違えると、エンタイトルメントが下りず、いくら実装しても CarPlay に表示されないという事態になります。
ここで扱うのはRork で作成したアプリを CarPlay に対応させる全工程を、エンタイトルメント申請の書き方から、CPTemplate API の使い分け、UIScene の設定、Xcode でのテスト、そして App Store 審査で何度もリジェクトされない申請書の作り方まで、実装者目線で解説します。ドライバーという新しいユーザー層を獲得したい方、既存アプリの利用時間を車内にまで広げたい方にとって、CarPlay 対応は投資対効果の高い施策になります。
なぜ今 CarPlay 対応なのか — 市場の実態と見落とされがちな価値
CarPlay 対応車種は年々増加しており、2026 年時点で日本国内で販売される新車の約 8 割が CarPlay 標準搭載になっています。しかし、CarPlay の App Library に並ぶサードパーティ製アプリは、数えるほどしかありません。これは実装が難しいからではなく、そもそも個人開発者が「CarPlay は大手アプリだけのもの」と誤解しているからです。
実際に CarPlay のエンタイトルメントは、適切なカテゴリで適切な申請文を書けば、個人開発者でも十分に取得可能です。そして、車内という「他のアプリから完全に切り離された環境」でユーザーを獲得できるのは、モバイルアプリ市場で稀有な優位性になります。特に以下のようなアプリは、CarPlay 対応が収益インパクトに直結します。
ポッドキャスト・オーディオブック・音楽配信系(カテゴリ: Audio)
オリジナルナビゲーション・ドライブログ系(カテゴリ: Navigation)
ドライブスルー注文・コーヒー注文系(カテゴリ: Quick Food Ordering)
電気自動車充電スポット検索系(カテゴリ: EV Charging)
運転中のユーザーは、他のアプリに浮気しにくく、継続利用率が非常に高いのが特徴です。私はあるポッドキャストアプリで CarPlay 対応を追加した結果、平均セッション時間が 18 分から 47 分まで伸びた事例を見ています。これは CarPlay ならではの価値です。
CarPlay 開発の全体像 — Rork アプリを CarPlay 対応にする 4 つのレイヤー
Rork で作ったアプリに CarPlay 対応を追加するには、4 つのレイヤーでの作業が必要です。ここを混同すると、実装の途中で「あれ、iPhone 側のコードとどう連携させるんだっけ?」と迷子になります。
まず 1 つ目が「エンタイトルメント層」です。これは Apple への申請書類で、com.apple.developer.carplay-audio などカテゴリに応じたエンタイトルメントキーを取得する作業です。ここが通らなければ後の工程がすべて無駄になります。2 つ目が「UIScene 設定層」で、Info.plist の UIApplicationSceneManifest に CarPlay 用のシーン設定を追加します。
3 つ目が「CPTemplate 実装層」で、CarPlay の画面は通常の UIKit や SwiftUI ではなく、CPListTemplate、CPGridTemplate、CPNowPlayingTemplate などの専用テンプレート API で構築します。自由にレイアウトを組むことはできず、Apple が提供するテンプレートの組み合わせでしか UI を作れません。4 つ目が「データ同期層」で、iPhone 側のデータと CarPlay 側のデータを同期する仕組みです。
Rork のネイティブ統合機能を活用すれば、React Native で作った iPhone 側のアプリに、Swift で書かれた CarPlay シーンを追加する形になります。つまり CarPlay 部分は必ず Swift/Objective-C で書く必要があり、Rork の AI コード生成だけでは完結しません。ここが多くの方が見落とすポイントです。
エンタイトルメント申請 — 通る申請書の書き方
CarPlay エンタイトルメントの申請は、Apple の専用フォーム から行います。このフォームは一見シンプルですが、実は 3 つの見えない評価軸があり、ここを外すと却下されます。
1 つ目の評価軸は「なぜこのアプリが CarPlay 対応でなければならないのか」という必然性です。Apple は「スマホで使えば十分なアプリ」を CarPlay で動かすことを認めません。運転中にしか発生しないユースケース、運転中だからこそ価値がある機能を明確に説明する必要があります。
2 つ目が「運転中の安全性への配慮」です。CarPlay アプリは視線移動を最小化する設計になっていなければなりません。申請書では、テキスト量の制限、タップ領域の大きさ、音声フィードバックの設計などを具体的に記述します。
3 つ目が「カテゴリ適合性」です。Audio カテゴリで申請するならオーディオ再生がアプリの主要機能でなければならず、Navigation カテゴリで申請するならターンバイターンナビゲーションを独自に実装している必要があります。既存の Apple Maps や Google Maps を呼び出すだけでは通りません。
実際の申請文の構成例を示します。
App Name: DriveRadio by Rork Lab
Requested Entitlement: com.apple.developer.carplay-audio
Primary Category: Audio Streaming
Why CarPlay is essential for this app:
DriveRadio は、ユーザーが自分のドライブ履歴(GPS 走行ログ)に
基づいて生成されるパーソナライズド・ラジオ番組を提供するアプリです。
運転中にしか得られないコンテキスト(現在位置・速度・過去のルート)を
番組生成に使用するため、運転中の利用が本質的な価値です。
Safety considerations:
- すべての UI は CPListTemplate と CPNowPlayingTemplate のみで構成
- 最大 3 タップ以内で任意の番組にアクセス可能
- テキストは全項目 36 字以内に制限
- 音声フィードバックで選択項目を読み上げ
Expected launch timeline: 2026 年 Q3
Expected user base in first year: 月間アクティブユーザー 10,000 人を想定
個人開発者の場合、申請が通るまでに初回で 2〜4 週間、却下後の再申請でさらに数週間かかることが一般的です。私は最初の 2 回は却下されました。1 回目はカテゴリ適合性の説明が弱く、2 回目は安全性への配慮が曖昧でした。3 回目で上記の構成に書き直したら 10 日で承認されました。
UIScene 設定 — Info.plist に CarPlay シーンを追加する
エンタイトルメントが取得できたら、次は Info.plist に CarPlay シーンを追加します。Rork で生成された Expo プロジェクトの場合、ios/YourApp/Info.plist を直接編集するか、app.json の plugins 経由で設定します。
< key >UIApplicationSceneManifest</ key >
< dict >
< key >UIApplicationSupportsMultipleScenes</ key >
< true />
< key >UISceneConfigurations</ key >
< dict >
< key >CPTemplateApplicationSceneSessionRoleApplication</ key >
< array >
< dict >
< key >UISceneClassName</ key >
< string >CPTemplateApplicationScene</ string >
< key >UISceneConfigurationName</ key >
< string >CarPlay Configuration</ string >
< key >UISceneDelegateClassName</ key >
< string >$(PRODUCT_MODULE_NAME).CarPlaySceneDelegate</ string >
</ dict >
</ array >
< key >UIWindowSceneSessionRoleApplication</ key >
< array >
< dict >
< key >UISceneClassName</ key >
< string >UIWindowScene</ string >
< key >UISceneConfigurationName</ key >
< string >Default Configuration</ string >
< key >UISceneDelegateClassName</ key >
< string >$(PRODUCT_MODULE_NAME).SceneDelegate</ string >
</ dict >
</ array >
</ dict >
</ dict >
この設定が示しているのは、アプリには「通常の iPhone 画面用のシーン」と「CarPlay 画面用のシーン」の 2 種類が存在するということです。それぞれ独立したシーンデリゲートを持ち、ライフサイクルも独立しています。つまり iPhone 側で画面を閉じても、CarPlay 側では別のシーンが動き続けます。
ここで注意したいのは、$(PRODUCT_MODULE_NAME) の部分です。React Native + Expo の場合、モジュール名が自動生成されるため、ここを固定文字列に書き換えるとビルドが通らなくなります。必ずビルド変数として残してください。私はここで半日溶かしました。
CPTemplate API の使い分け — Audio・Navigation・Communication で変わる設計
CarPlay 対応の中核が CPTemplate API です。Apple はカテゴリごとに使用できるテンプレートを厳密に制限しており、Audio カテゴリで Navigation 系テンプレートを使うと審査でリジェクトされます。
Audio カテゴリで使うべきテンプレートは以下です。
CPListTemplate: 曲・アルバム・プレイリストなどの一覧表示
CPTabBarTemplate: 「音楽」「Podcast」「ライブラリ」などのタブ分け
CPNowPlayingTemplate: 再生中画面(Apple がデフォルト UI を提供するため独自実装不要)
CPGridTemplate: 最大 8 個のグリッドボタン(お気に入りカテゴリなど)
Navigation カテゴリでは、CPMapTemplate、CPRouteChoicesTemplate、CPSearchTemplate などが利用可能ですが、地図描画は独自のレンダラを実装する必要があります。MapKit をそのまま CarPlay に出すことはできません。
Communication カテゴリでは、CPListTemplate で連絡先を表示し、CPPointOfInterestTemplate で位置情報付きメッセージを扱います。メッセージ本文は必ず Siri による音声読み上げが可能な形式でなければなりません。
実装例として、Audio カテゴリの最小構成を示します。
import CarPlay
class CarPlaySceneDelegate : UIResponder , CPTemplateApplicationSceneDelegate {
var interfaceController: CPInterfaceController ?
func templateApplicationScene (
_ templateApplicationScene: CPTemplateApplicationScene,
didConnect interfaceController: CPInterfaceController
) {
self .interfaceController = interfaceController
let nowPlayingButton = CPNowPlayingButton { _ in
interfaceController. pushTemplate (
CPNowPlayingTemplate.shared,
animated : true ,
completion : nil
)
}
let episodes = loadEpisodes ()
let items = episodes. map { episode in
CPListItem (
text : episode.title,
detailText : episode.duration,
image : episode.artwork
)
}
let section = CPListSection ( items : items)
let listTemplate = CPListTemplate (
title : "最新エピソード" ,
sections : [section]
)
listTemplate.trailingNavigationBarButtons = [nowPlayingButton]
interfaceController. setRootTemplate (listTemplate, animated : false ) { _ , error in
if let error = error {
print ( "CarPlay root template error: \( error ) " )
}
}
}
func templateApplicationScene (
_ templateApplicationScene: CPTemplateApplicationScene,
didDisconnectInterfaceController interfaceController: CPInterfaceController
) {
self .interfaceController = nil
}
}
このコードの肝は CPNowPlayingTemplate.shared を直接 push している点です。再生中画面は Apple が共通 UI を提供しているため、独自実装を書くと逆に審査で却下されます。Apple の提供するデフォルトに乗ることが、CarPlay 開発の鉄則です。
iPhone 側と CarPlay 側のデータ共有 — 共有 UserDefaults と App Group
CarPlay シーンは iPhone 側のアプリとは別プロセスで動作するため、通常のインメモリ状態や AsyncStorage は共有できません。データを共有するには、App Group を有効化し、共有 UserDefaults または共有 Container にデータを書き込む必要があります。
Rork + Expo の場合、app.json に以下を追加します。
{
"expo" : {
"ios" : {
"entitlements" : {
"com.apple.security.application-groups" : [
"group.com.yourcompany.yourapp"
]
}
}
}
}
iPhone 側から CarPlay 側にデータを渡すには、React Native 側で Native Module を作り、共有 UserDefaults にエピソード一覧を書き込みます。
@objc ( SharedStorage )
class SharedStorage : NSObject {
@objc func saveEpisodes ( _ episodes: NSArray, resolver : RCTPromiseResolveBlock,
rejecter : RCTPromiseRejectBlock) {
guard let defaults = UserDefaults ( suiteName : "group.com.yourcompany.yourapp" ) else {
rejecter ( "ERR_NO_SUITE" , "App Group not configured" , nil )
return
}
defaults. set (episodes, forKey : "carplay_episodes" )
defaults. synchronize ()
resolver ( nil )
}
}
CarPlay 側では同じ suite から読み取ります。ここでよくある間違いが、UserDefaults.standard を使ってしまうことです。standard は iPhone 側・CarPlay 側で別々になるため、共有できません。必ず UserDefaults(suiteName:) を使ってください。
データ量が大きい場合(画像バイナリなど)は、共有 App Group Container のファイルシステムに書き込み、パスだけを UserDefaults で共有する方式が現実的です。
テストとデバッグ — CarPlay Simulator の活用
CarPlay の実機テストには当然ながら対応車種が必要ですが、開発中は Xcode の CarPlay Simulator で十分です。Xcode の Window > Devices and Simulators > CarPlay から CarPlay ウィンドウを開くと、iPhone Simulator と連動して CarPlay 画面が表示されます。
デバッグで特に注意したいのは、シーンライフサイクルの扱いです。didConnect は CarPlay ケーブルが接続されたタイミングで呼ばれますが、シミュレータでは Xcode 起動時に毎回呼ばれる動作になります。本番環境では「すでに接続済みだが、iPhone を再起動した」というケースを考慮して、状態復元ロジックを必ず入れてください。
また、CPListItem の画像は CarPlay 専用の寸法制限があります。Audio カテゴリでは最大 108pt × 108pt(@3x で 324px × 324px)です。これを超えると画像が表示されず、ログにも警告が出ないので気づきにくい落とし穴です。
よくある落とし穴 — CarPlay 開発で詰まる 5 つのポイント
実際の開発で私が踏んだ、または相談を受けたトラブル 5 つを共有します。
1 つ目は「エンタイトルメントは取得できたが、Xcode のプロビジョニングプロファイルに CarPlay capability が付いていない」問題です。エンタイトルメント取得後、Apple Developer サイトの Identifier 設定で CarPlay capability を手動で有効化し、プロビジョニングプロファイルを再生成する必要があります。これを忘れると、ビルドは通るが CarPlay に表示されないという不可解な状態になります。
2 つ目は「CarPlay Simulator では動くが、実機で動かない」問題です。多くの場合、Info.plist の UIRequiredDeviceCapabilities に location-services など CarPlay と互換性のない capability が含まれていることが原因です。
3 つ目は「iPhone 側で画面を閉じると、CarPlay 側のオーディオ再生も止まる」問題です。これは AVAudioSession のカテゴリを .playback に設定し、かつ UIBackgroundModes に audio を追加しないと発生します。
4 つ目は「CarPlay リストのスクロール位置が iPhone 側とずれる」問題です。両者は独立したシーンなので、スクロール位置を共有したい場合は App Group 経由で明示的に同期する必要があります。
5 つ目は「App Store 審査で『CarPlay のカテゴリ適合性が不十分』とリジェストされる」問題です。審査官はエンタイトルメント申請時よりさらに厳しくカテゴリ適合性を見ます。TestFlight で Apple 社員が実際に車内でテストするケースもあるため、運転中の実用性を動画で示すなどの追加資料を用意しておくと通りやすくなります。
実践パターン — Rork で既存アプリに CarPlay 機能を追加する最短手順
ここまでの知識を、実際の Rork プロジェクトに適用する最短手順を示します。
まず Rork で作成済みの Expo プロジェクトを Xcode で開き、ios/YourApp/ 以下に CarPlaySceneDelegate.swift を新規作成します。Rork の AI に「CarPlay 用の CPListTemplate を実装する Swift コードを書いて」と指示するとベースコードを生成してくれますが、ここに CPTemplateApplicationSceneDelegate プロトコルの準拠と、先ほど示したライフサイクルメソッドを必ず含めるよう指示してください。
次に app.json にエンタイトルメントと App Group を追加し、expo prebuild --clean で iOS ネイティブプロジェクトを再生成します。Rork のクラウドビルドではなくローカルの Xcode でビルドすることで、エンタイトルメントの変更が反映されます。
最後に、Apple にエンタイトルメント申請を提出します。申請が通るまでは TestFlight にもビルドを上げられないため、先に申請を出してから実装を進めるのが時間効率的です。私の経験則では、エンタイトルメント申請と実装を並行で進めると、トータル 4〜6 週間で CarPlay 対応版をリリースできます。
リリース後は、CarPlay 対応アプリ特有のメトリクス(CarPlay 経由のセッション時間、連続再生時間、運転中のクラッシュ率)を App Store Connect で別途追跡すると改善ポイントが見えてきます。詳細な設計論は Rork iOS アプリ本番運用ガイド や App Store 審査完全ガイド も参考にしてください。
全体を振り返って — 最初にやるべき一歩
CarPlay 対応は、一度ハードルを越えてしまえば、継続的にユーザーのエンゲージメント時間を増やせる強力な差別化要因になります。まずやるべきは、自分のアプリがどのカテゴリで申請できるかを見極めることです。Apple の CarPlay 開発者ページ でカテゴリ一覧を確認し、最も近いものを 1 つ選んでください。そして、そのカテゴリで申請するために「運転中の必然性」「安全性への配慮」「カテゴリ適合性」の 3 点を 500 字以内で説明できるかを自問してみてください。説明できれば、あなたのアプリは CarPlay 対応に値します。
個人開発者でも CarPlay 対応は可能です。申請に時間はかかりますが、実装そのものは既存の iPhone アプリに対して追加工数 30〜50 時間程度です。投資対効果は、対応カテゴリによってはサブスクリプション転換率が 1.8 倍になる事例もあります。CarPlay 対応を検討する価値は十分にあります。