How to install carplay
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill carplayFull instructions (SKILL.md)
Source of truth, from dpearson2699/swift-ios-skills.
name: carplay description: "Build CarPlay-enabled apps using the CarPlay framework. Use when creating navigation, audio, communication, EV charging, parking, or food ordering apps for the car display, working with CPTemplateApplicationScene, CPInterfaceController template hierarchies, CPListTemplate, CPMapTemplate, CPNowPlayingTemplate, configuring CarPlay entitlements, or integrating with CarPlay Simulator for testing."
CarPlay
Build apps that display on the vehicle's CarPlay screen using the CarPlay framework's template-based UI system. Covers scene lifecycle, template types, navigation guidance, audio playback, communication, point-of-interest categories, entitlement setup, and simulator testing. Targets Swift 6.3 / iOS 26+.
See references/carplay-patterns.md for extended patterns including full navigation sessions, dashboard scenes, and advanced template composition.
Scope boundary: full CarPlay framework apps use category entitlements,
CPTemplateApplicationScene, CPTemplateApplicationSceneDelegate,
CPInterfaceController, and system CPTemplate navigation. CarPlay-visible
WidgetKit widgets and ActivityKit Live Activities are separate system
experiences; route their implementation to those domains while keeping
CarPlay-specific validation here.
Contents
- Entitlements and Setup
- Scene Configuration
- Templates Overview
- Navigation Apps
- Audio Apps
- Communication Apps
- Point of Interest Apps
- Testing with CarPlay Simulator
- Common Mistakes
- Review Checklist
- References
Entitlements and Setup
CarPlay requires a category-specific entitlement granted by Apple. Request it at developer.apple.com/contact/carplay and agree to the CarPlay Entitlement Addendum.
Entitlement Keys by Category
| Entitlement | Category |
|---|---|
com.apple.developer.carplay-audio | Audio |
com.apple.developer.carplay-communication | Communication |
com.apple.developer.carplay-maps | Navigation |
com.apple.developer.carplay-charging | EV Charging |
com.apple.developer.carplay-parking | Parking |
com.apple.developer.carplay-quick-ordering | Quick Food Ordering |
Project Configuration
- Update the App ID in the developer portal under Additional Capabilities.
- Generate a new provisioning profile for the updated App ID.
- In Xcode, disable automatic signing and import the CarPlay provisioning profile.
- Add an
Entitlements.plistwith the entitlement key set totrue. - Set Code Signing Entitlements build setting to the
Entitlements.plistpath.
Key Types
| Type | Role |
|---|---|
CPTemplateApplicationScene | UIScene subclass for the CarPlay display |
CPTemplateApplicationSceneDelegate | Scene connect/disconnect lifecycle |
CPInterfaceController | CarPlay-provided controller for setting the root template and pushing, presenting, or popping templates |
CPTemplate | Abstract base for all CarPlay templates |
CPSessionConfiguration | Vehicle display limits and content style |
Scene Configuration
Declare the CarPlay scene in Info.plist and implement
CPTemplateApplicationSceneDelegate to respond when CarPlay connects.
Info.plist Scene Manifest
<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>CarPlaySceneConfiguration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).CarPlaySceneDelegate</string>
</dict>
</array>
</dict>
</dict>
Scene Delegate (Non-Navigation)
Non-navigation apps receive an interface controller only. No window.
import CarPlay
final class CarPlaySceneDelegate: UIResponder,
CPTemplateApplicationSceneDelegate {
var interfaceController: CPInterfaceController?
func templateApplicationScene(
_ templateApplicationScene: CPTemplateApplicationScene,
didConnect interfaceController: CPInterfaceController
) {
self.interfaceController = interfaceController
interfaceController.setRootTemplate(buildRootTemplate(),
animated: true, completion: nil)
}
func templateApplicationScene(
_ templateApplicationScene: CPTemplateApplicationScene,
didDisconnectInterfaceController interfaceController: CPInterfaceController
) {
self.interfaceController = nil
}
}
Scene Delegate (Navigation)
Navigation apps receive both an interface controller and a CPWindow.
Set the window's root view controller to draw map content.
func templateApplicationScene(
_ templateApplicationScene: CPTemplateApplicationScene,
didConnect interfaceController: CPInterfaceController,
to window: CPWindow
) {
self.interfaceController = interfaceController
self.carWindow = window
window.rootViewController = MapViewController()
let mapTemplate = CPMapTemplate()
mapTemplate.mapDelegate = self
interfaceController.setRootTemplate(mapTemplate, animated: true,
completion: nil)
}
Templates Overview
CarPlay provides a fixed set of template types. The app supplies content; the system renders it on the vehicle display.
General Purpose Templates
| Template | Purpose |
|---|---|
CPTabBarTemplate | Container with tabbed child templates |
CPListTemplate | Scrollable sectioned list |
CPGridTemplate | Grid of tappable icon buttons (max 8) |
CPInformationTemplate | Key-value info with up to 3 actions |
CPAlertTemplate | Modal alert with up to 2 actions |
CPActionSheetTemplate | Modal action sheet |
Category-Specific Templates
| Template | Category |
|---|---|
CPMapTemplate | Navigation -- map overlay with nav bar |
CPSearchTemplate | Navigation -- destination search |
CPNowPlayingTemplate | Audio -- shared Now Playing screen |
CPPointOfInterestTemplate | EV Charging / Parking / Food -- POI map |
CPContactTemplate | Communication -- contact card |
Navigation Hierarchy
Use pushTemplate(_:animated:completion:) to add templates to the stack.
Use presentTemplate(_:animated:completion:) for modal display.
Use popTemplate(animated:completion:) to go back.
CPTabBarTemplate must be set as root -- it cannot be pushed or presented.
CPTabBarTemplate
let browseTab = CPListTemplate(title: "Browse",
sections: [CPListSection(items: listItems)])
browseTab.tabImage = UIImage(systemName: "list.bullet")
let tabBar = CPTabBarTemplate(templates: [browseTab, settingsTab])
tabBar.delegate = self
interfaceController.setRootTemplate(tabBar, animated: true, completion: nil)
CPListTemplate
let item = CPListItem(text: "Favorites", detailText: "12 items")
item.handler = { selectedItem, completion in
self.interfaceController?.pushTemplate(detailTemplate, animated: true,
completion: nil)
completion()
}
let section = CPListSection(items: [item], header: "Library",
sectionIndexTitle: nil)
let listTemplate = CPListTemplate(title: "My App", sections: [section])
Navigation Apps
Navigation apps use com.apple.developer.carplay-maps. They are the only
category that receives a CPWindow for drawing map content. The root
template must be a CPMapTemplate.
Trip Preview and Route Selection
let routeChoice = CPRouteChoice(
summaryVariants: ["Fastest Route", "Fast"],
additionalInformationVariants: ["Via Highway 101"],
selectionSummaryVariants: ["25 min"]
)
let trip = CPTrip(origin: origin, destination: destination,
routeChoices: [routeChoice])
mapTemplate.showTripPreviews([trip], textConfiguration: nil)
Starting a Navigation Session
extension CarPlaySceneDelegate: CPMapTemplateDelegate {
func mapTemplate(_ mapTemplate: CPMapTemplate,
startedTrip trip: CPTrip,
using routeChoice: CPRouteChoice) {
let session = mapTemplate.startNavigationSession(for: trip)
session.pauseTrip(for: .loading, description: "Calculating route...")
let maneuver = CPManeuver()
maneuver.instructionVariants = ["Turn right onto Main St"]
maneuver.symbolImage = UIImage(systemName: "arrow.turn.up.right")
session.upcomingManeuvers = [maneuver]
let estimates = CPTravelEstimates(
distanceRemaining: Measurement(value: 5.2, unit: .miles),
timeRemaining: 900)
session.updateEstimates(estimates, for: maneuver)
}
}
Map Buttons
let zoomIn = CPMapButton { _ in self.mapViewController.zoomIn() }
zoomIn.image = UIImage(systemName: "plus.magnifyingglass")
mapTemplate.mapButtons = [zoomIn, zoomOut]
CPSearchTemplate
extension CarPlaySceneDelegate: CPSearchTemplateDelegate {
func searchTemplate(_ searchTemplate: CPSearchTemplate,
updatedSearchText searchText: String,
completionHandler: @escaping ([CPListItem]) -> Void) {
performSearch(query: searchText) { results in
completionHandler(results.map {
CPListItem(text: $0.name, detailText: $0.address)
})
}
}
func searchTemplate(_ searchTemplate: CPSearchTemplate,
selectedResult item: CPListItem,
completionHandler: @escaping () -> Void) {
// Navigate to selected destination
completionHandler()
}
}
Audio Apps
Audio apps use com.apple.developer.carplay-audio. They display browsable
content in lists and use CPNowPlayingTemplate for playback controls.
CPInformationTemplate is not available to audio-entitled apps.
Now Playing Template
CPNowPlayingTemplate is a shared singleton. It reads metadata from
MPNowPlayingInfoCenter. Do not instantiate a new one.
let nowPlaying = CPNowPlayingTemplate.shared
nowPlaying.isUpNextButtonEnabled = true
nowPlaying.isAlbumArtistButtonEnabled = true
nowPlaying.updateNowPlayingButtons([
CPNowPlayingShuffleButton { _ in self.toggleShuffle() },
CPNowPlayingRepeatButton { _ in self.toggleRepeat() }
])
nowPlaying.add(self) // Register as CPNowPlayingTemplateObserver
Siri Assistant Cell
Audio apps supporting INPlayMediaIntent can show an assistant cell.
Communication apps use INStartCallIntent with .startCall.
let config = CPAssistantCellConfiguration(
position: .top, visibility: .always, assistantAction: .playMedia)
let listTemplate = CPListTemplate(
title: "Playlists",
sections: [CPListSection(items: items)],
assistantCellConfiguration: config)
Communication Apps
Communication apps use com.apple.developer.carplay-communication.
They display message lists and contacts, and support INStartCallIntent
for Siri-initiated calls.
CPMessageListItem has no app-provided selection handler. When selected,
CarPlay invokes Siri compose, read, or reply behavior based on the item's
phone/email, unread state, or existing conversation configuration.
let leading = CPMessageListItemLeadingConfiguration(
leadingItem: .star, leadingImage: nil, unread: true)
let trailing = CPMessageListItemTrailingConfiguration(
trailingItem: .none, trailingImage: nil)
let message = CPMessageListItem(
conversationIdentifier: "conv-123",
text: "Jane",
leadingConfiguration: leading,
trailingConfiguration: trailing,
detailText: "Meeting at 3pm",
trailingText: "2:45 PM")
let messageList = CPListTemplate(title: "Messages",
sections: [CPListSection(items: [message])])
Point of Interest Apps
EV charging, parking, and food ordering apps use CPPointOfInterestTemplate
and CPInformationTemplate to display locations and details.
CPPointOfInterestTemplate displays a maximum of 12 points of interest.
CPPointOfInterestTemplate
let poi = CPPointOfInterest(
location: MKMapItem(placemark: MKPlacemark(
coordinate: CLLocationCoordinate2D(latitude: 37.7749,
longitude: -122.4194))),
title: "SuperCharger Station", subtitle: "4 available",
summary: "150 kW DC fast charging",
detailTitle: "SuperCharger Station", detailSubtitle: "$0.28/kWh",
detailSummary: "Open 24 hours",
pinImage: UIImage(systemName: "bolt.fill"))
poi.primaryButton = CPTextButton(title: "Navigate",
textStyle: .confirm) { _ in }
let poiTemplate = CPPointOfInterestTemplate(
title: "Nearby Chargers", pointsOfInterest: [poi], selectedIndex: 0)
poiTemplate.pointOfInterestDelegate = self
CPInformationTemplate
let infoTemplate = CPInformationTemplate(
title: "Order Summary", layout: .leading,
items: [
CPInformationItem(title: "Item", detail: "Burrito Bowl"),
CPInformationItem(title: "Total", detail: "$12.50")],
actions: [
CPTextButton(title: "Place Order", textStyle: .confirm) { _ in
self.placeOrder() },
CPTextButton(title: "Cancel", textStyle: .cancel) { _ in
self.interfaceController?.popTemplate(animated: true,
completion: nil) }])
Testing with CarPlay Simulator
- Build and run in Xcode with the iOS simulator.
- Choose I/O > External Displays > CarPlay.
Default window: 800x480 at @2x. Enable extra options for navigation apps:
defaults write com.apple.iphonesimulator CarPlayExtraOptions -bool YES
Recommended Test Configurations
| Configuration | Pixels | Scale |
|---|---|---|
| Minimum | 748 x 456 | @2x |
| Portrait | 768 x 1024 | @2x |
| Standard | 800 x 480 | @2x |
| High-resolution | 1920 x 720 | @3x |
Simulator cannot test locked-iPhone behavior, Siri, audio coexistence with car radio, or physical input hardware (knobs, touch pads). Test on a real CarPlay-capable vehicle or aftermarket head unit when possible. Design primary CarPlay flows so they do not require iPhone input while CarPlay is active.
Common Mistakes
DON'T: Use the wrong scene delegate method
Navigation apps must implement templateApplicationScene(_:didConnect:to:)
(with CPWindow). Non-navigation apps use
templateApplicationScene(_:didConnect:) (no window). Using the wrong
variant produces no CarPlay UI.
DON'T: Draw custom UI in the navigation window
CPWindow is exclusively for map content. All overlays, alerts, and
controls must use CarPlay templates.
DON'T: Push or present CPTabBarTemplate
CPTabBarTemplate can only be set as root. Pushing or presenting it fails.
Use setRootTemplate(_:animated:completion:).
DON'T: Instantiate CPNowPlayingTemplate
Use CPNowPlayingTemplate.shared. Creating a new instance causes issues.
DON'T: Add handlers to CPMessageListItem
CPMessageListItem is Siri-managed, unlike CPListItem. Do not set
message.handler; use the item configuration and userInfo for context.
DON'T: Treat widgets as CarPlay template apps
CarPlay-visible widgets and Live Activities belong to WidgetKit and ActivityKit. Use this skill for category-entitled CarPlay template app scenes and for validating those surfaces in the car context.
DON'T: Ignore vehicle display limits
Check CPSessionConfiguration.limitedUserInterfaces and respect
maximumItemCount / maximumSectionCount on list templates.
DON'T: Forget to call the completion handler
CPListItem.handler must call its completion handler in every code path.
Failure leaves the list in a loading state.
Review Checklist
- Correct CarPlay entitlement key in
Entitlements.plist -
UIApplicationSupportsMultipleScenesset totrue -
CPTemplateApplicationSceneSessionRoleApplicationscene in Info.plist - Scene delegate class name matches
UISceneDelegateClassName - Correct delegate method used (with/without
CPWindow) - Root template set in
didConnectbefore returning - Interface controller and window references cleared on disconnect
-
CPTabBarTemplateonly used as root, never pushed -
CPNowPlayingTemplate.sharedused, not a new instance - Communication rows use
CPMessageListItemwithout custom handlers - WidgetKit/ActivityKit surfaces routed outside CarPlay template app code
-
maximumItemCount/maximumSectionCountchecked before populating lists -
CPListItem.handlercalls completion in every path - Map-only content in
CPWindowroot view controller (navigation apps) - App functions while iPhone is locked
- Tested at minimum, standard, and high-resolution simulator sizes
- Audio session deactivated when not actively playing
References
- Extended patterns (dashboard, instrument cluster, full nav flow, tab composition): references/carplay-patterns.md
- CarPlay framework
- CPTemplateApplicationSceneDelegate
- CPInterfaceController
- CPMapTemplate
- CPListTemplate
- CPNowPlayingTemplate
- CPPointOfInterestTemplate
- CPNavigationSession
- Requesting CarPlay Entitlements
- Displaying Content in CarPlay
- Using the CarPlay Simulator
- CarPlay HIG
Related skills
More from dpearson2699/swift-ios-skills and the wider catalog.
swiftui-animation
Implement, review, or improve SwiftUI animations and transitions. Use when adding explicit animations with withAnimation, configuring implicit animations with .animation(_:body:) or .animation(_:value:), configuring spring animations (.smooth, .snappy, .bouncy), building phase or keyframe animations with PhaseAnimator/KeyframeAnimator, creating hero transitions with matchedGeometryEffect or matchedTransitionSource, adding SF Symbol effects (iOS 17 bounce, pulse, variableColor, scale, appear, disappear, replace; iOS 18 breathe, rotate, wiggle), implementing custom Transition or CustomAnimation types, or ensuring animations respect accessibilityReduceMotion.
ios-accessibility
Implements, reviews, or improves accessibility in iOS/macOS apps with SwiftUI, UIKit, and AppKit. Use when adding VoiceOver, Voice Control, Switch Control, or Full Keyboard Access support; when working with accessibility labels, hints, values, traits, accessibilityInputLabels, NSAccessibility, grouping, reading order, accessibility focus restoration with @AccessibilityFocusState, Dynamic Type, @ScaledMetric, custom rotors, accessibility actions, XCTest accessibility checks, App Store Accessibility Nutrition Labels, App Store Connect accessibility answers, a11y compliance audits, or system accessibility preferences.
swiftui-patterns
Builds and reviews SwiftUI views with modern MV architecture, state management, view composition, and migration/availability guidance. Covers @Observable ownership rules, @State/@Bindable/@Environment wiring, view decomposition, custom ViewModifiers, environment values, async data loading with .task, iOS 26+ handoff reminders, Writing Tools, clipboard availability caveats, and performance guidelines. Use when structuring SwiftUI app state, managing @Observable, composing view hierarchies, or correcting SwiftUI pattern guidance.
swiftui-performance
Audit and improve SwiftUI runtime performance. Use when diagnosing slow rendering, janky scrolling, high CPU, memory usage, excessive view updates, layout thrash, body evaluation cost, identity churn, view lifetime issues, lazy loading, Instruments profiling guidance, and performance audit requests.
app-store-review
Prepare for App Store review and prevent rejections. Covers App Store review guidelines, app rejection reasons, PrivacyInfo.xcprivacy privacy manifest requirements, required API reason codes, in-app purchase IAP and StoreKit rules, App Store Guidelines compliance, ATT App Tracking Transparency, EU DMA Digital Markets Act, HIG compliance checklist, app submission preparation, review preparation, metadata requirements, entitlements, widgets, and Live Activities review rules. Use when preparing for App Store submission, fixing rejection reasons, auditing privacy manifests, implementing ATT consent flow, configuring StoreKit IAP, or checking HIG compliance.
ios-networking
Build, review, or improve networking code in iOS/macOS apps using URLSession with async/await, structured concurrency, and modern Swift patterns. Use when working with REST APIs, downloading files, uploading data, WebSocket connections, pagination, retry logic, request middleware, caching, background transfers, or network reachability monitoring. Also use when handling HTTP requests, API clients, network error handling, or data fetching in Swift apps.