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.
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.plist
with the entitlement key set to
true
.
Set Code Signing Entitlements build setting to the
Entitlements.plist
path.
Key Types
Type
Role
CPTemplateApplicationScene
UIScene subclass for the CarPlay display
CPTemplateApplicationSceneDelegate
Scene connect/disconnect lifecycle
CPInterfaceController
Manages the template navigation hierarchy
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
UIApplicationSceneManifest
UIApplicationSupportsMultipleScenes
UISceneConfigurations
CPTemplateApplicationSceneSessionRoleApplication
UISceneClassName
CPTemplateApplicationScene
UISceneConfigurationName
CarPlaySceneConfiguration
UISceneDelegateClassName
$(PRODUCT_MODULE_NAME).CarPlaySceneDelegate
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.
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.
let
message
=
CPMessageListItem
(
conversationIdentifier
:
"conv-123"
,
text
:
"Meeting at 3pm"
,
leadingConfiguration
:
CPMessageListItem
.
LeadingConfiguration
(
leadingItem
:
.
init
(
text
:
"Jane"
,
textStyle
:
.
abbreviated
)
,
unread
:
true
)
,
trailingConfiguration
:
CPMessageListItem
.
TrailingConfiguration
(
trailingItem
:
.
init
(
text
:
"2:45 PM"
)
)
,
trailingText
:
nil
,
trailingImage
:
nil
)
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
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.
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: 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
UIApplicationSupportsMultipleScenes
set to
true
CPTemplateApplicationSceneSessionRoleApplication
scene in Info.plist
Scene delegate class name matches
UISceneDelegateClassName
Correct delegate method used (with/without
CPWindow
)
Root template set in
didConnect
before returning
Interface controller and window references cleared on disconnect
CPTabBarTemplate
only used as root, never pushed
CPNowPlayingTemplate.shared
used, not a new instance
maximumItemCount
/
maximumSectionCount
checked before populating lists
CPListItem.handler
calls completion in every path
Map-only content in
CPWindow
root 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