carplay

Build CarPlay-enabled apps using the CarPlay framework. Use when creating navigation, audio, communication, EV charging, parking, or food ordering apps for the…

INSTALLATION
npx skills add https://github.com/dpearson2699/swift-ios-skills --skill carplay
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

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](#entitlements-and-setup)
  • [Scene Configuration](#scene-configuration)
  • [Templates Overview](#templates-overview)
  • [Navigation Apps](#navigation-apps)
  • [Audio Apps](#audio-apps)
  • [Communication Apps](#communication-apps)
  • [Point of Interest Apps](#point-of-interest-apps)
  • [Testing with CarPlay Simulator](#testing-with-carplay-simulator)
  • [Common Mistakes](#common-mistakes)
  • [Review Checklist](#review-checklist)
  • [References](#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

<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.

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

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card