Enhance Existing Apps with CarPlay
CarPlay is an extension of iOS that enables a car’s infotainment system to display and control a range of apps. Streaming music, dialling in to a meeting, finding a car park, and ordering food, are just some of the apps you might consider enhancing to take advantage of the maturing CarPlay platform. Below I’ll cover the steps required to display a simple CarPlay screen, including.
CarPlay Entitlement
Render phone’s UI inside Scene
Setting up the CarPlay Scene
Extending React Native with CarPlay
CarPlay Entitlement
In order to develop for CarPlay an appropriate entitlement is required. This entitlement must be requested from Apple, and only a limited range of app categories which are supported: Audio, Communication, Electric Vehicle Charging, Navigation, Parking and Quick Food Ordering (Refer to the CarPlay App Programming Guide for more information). Once your app has the correct CarPlay entitlement it should be visible on a CarPlay device.
Until you have an entitlement, you won’t be able to deploy your app — to an iPhone and see it on CarPlay in a car. In the meantime, you can get started with a simulator using the CarPlay external display. First you’ll need to create an Entitlements file to include the appropriate CarPlay entitlement.
Creating an Entitlements File
If you don’t already have an entitlements file, you can create one in Xcode via the following:
Select File> New > File
Select Property List
Select Next
Enter a name for the file e.g Entitlements
Select Create
Change the Type to Entitlements Plist
7. Change the file name to end with .entitlements as per the above screenshot
8. Open your Projects Build Settings
9. Ensure Code Signing Entitlements points to your created Entitlements file
Adding the CarPlay Entitlement
The Entitlements file needs to be updated to include the appropriate entitlement for CarPlay. CarPlay has different entitlements for each app category. Each app category determines which templates your CarPlay scene can access.
To add a CarPlay entitlement to the file we simply add the entitlements string which we can get from the CarPlay App Programming Guide. Set the type to boolean and give it a value of 1. The screenshot below shows how to use the parking entitlement that enables a parking app to be displayed on CarPlay.
Scenes
Scenes were introduced in iOS 13, allowing us to create multiple UI instances for the same app. These instances respond independently to each other but share the same memory and process space. CarPlay requires our app to be using scenes. The iPhone’s UI will be rendered in its own scene and CarPlay’s UI will be rendered in another scene.
Newer versions of Xcode create new Swift projects using scenes. If your project is already using them, you can skip the section below and jump to Setting up the CarPlay Scene.
Render the iPhone’s UI inside a Scene
To convert a project to use scenes all we need to do is create create an implementation of UIWindowSceneDelegate for the iPhone app, update info.plist to specify the scene manifest and modify AppDelegate to use scenes which is described in further detail below.
Creating a PhoneSceneDelegate
To create a UIWindowSceneDelegate for the iPhone, create a class which implements the UIResponder and UIWindowSceneDelegate and implement the scene function. This function will return a UIWindowScene object containing the root view to be displayed on the phone. The code snippet below shows an example using a Swift view.
Set Up the Application Scene Manifest
In order to use the scene created above we must specify the Application Scene Manifest property inside the info.plist file. This property determines which scenes are available and which delegates they will use. Set up the Application Scene Manifest similar to the screenshot below
Application Session Role should have a single element inside it defining the phones scene and have the following properties:
Configuration Name a unique string to identify this configuration.
Delegate Class Name the SceneDelegate class used to manage the scene.
AppDelegate changes
The AppDelegate will need to change to return the appropriate UISceneConfiguration object instead of rendering the UI itself. Below is an example of an AppDelegate which just returns the iPhone’s scene.
Setting up the CarPlay Scene
CarPlay uses an external scene and is set up differently to the iPhone’s scene. This will require a CPTemplateApplicationSceneDelegate implementation, modifications to Application Scene Manifest and modifications to AppSceneDelegate.
CarPlay Scene Delegate
The CarPlay framework is used for rendering CarPlay components, which means we must first create a scene delegate implementing the CPTemplateApplicationSceneDelegate protocol. Below is an example of displaying a simple “Hello CarPlay” screen.
The CPInterfaceController instance is used to set the root screen and can be used for any screen changes required. A CPInformationTemplate has been used to display text on the screen (Refer to theCarPlay App Programming Guide to see the different templates that can be used).
Modify Application Scene Manifest to Include CarPlay
In order to use the CarPlaySceneDelegate shown above we must modify the Application Scene Manifest in the info.plist file. The configuration should look similar to the below screenshot.
The Enable Multiple Windows property should be set to true as we want to display on the iPhone’s and CarPlay.
The External Display Session Role property specifies an array of external displays. We need to add an element to this array in order to specify our CarPlay scene. This element needs the following properties;
Class Name the name of the protocol the scene delegate implements for CarPlay this should be CPTemplateApplicationScene
Configuration Name a unique string to identify this configuration.
Delegate Class Name the name of CarPlay’s scene delegate.
App Delegate CarPlay Changes
At this point AppDelegate.swift is returning a default UISceneConfiguration for the iPhone’s UI. This needs to be extended to also support the request for a CarPlay scene. To do this we modify the application method to return the relevant UISceneConfiguration depending on whether the connectingSceneSession.role is for CarPlay or the iPhone, as per the code snippet below. The name parameter of each UISceneConfiguration must match the name of Delegate Class Name defined in the Application Scene Manifest.
The application function above will only instantiate the CarPlay scene when the app is launched through CarPlay. The iPhone scene will only be instantiated when the App is launched through the iPhone.
Launching CarPlay
We can now launch the iPhone app via Xcode by selecting the target device and running the project. In order to display the CarPlay simulator, navigate to external displays and select CarPlay, I/O > External Displays > CarPlay
On selecting the app, the following should render on the CarPlay simulator.
Extending React Native with CarPlay
The above demonstrates how to extend an existing swift app to support CarPlay. But what if your app is built with React Native? Is it still possible to integrate with CarPlay? As is typical of React Native, the answer is yes!
We being by modifying the example of PhoneSceneDelegate above to instantiate RCTRootView and its required RCTBridge. We then set RCTRootView as the root of the iPhone’s scene. This gives us a working React Native view on the phone and a CarPlay scene written in swift.
Both iPhone and CarPlay scenes need to be able to communicate with the React Native bundler, but if CarPlay has been launched first, the bundler will not be running.
In order to communicate with React Native without the iPhone’s scene, we need to instantiate the bundler when the app starts and use the same instance when the iPhone’s scene is instantiated.
To ensure the bundler is always present, we edit AppDelegate.swift to instantiate the RCTRootView when the app launches, rather than during the iPhone’s scene initialisation. The iPhone’s scene still needs to display this instance when its created. We cannot provide a parameter to the SceneDelegate, so we need to store the RCTBride and RCTRootView instances globally. For this purpose a singleton RNBridgeInstanceHolder can be been used. The code snipped below uses application function to create the RCTBridge and RCTRootView when the app launches.
React Native Phone Scene Delegate
The Phone’s scene delegate in the code snippet below is using the RCTRootView instance stored in the RNBridgeInstanceHolder singleton.
Communication Between the CarPlay Scene and Phone Scenes
Scenes share the same memory and app processing space. This means we can communicate between CarPlay and the phone scenes via events between Swift and React Native. This is similar to communicating between Swift and React Native components. Have a look at the React Native docs and refer to Sending Events to JavaScript for more information.
What Next?
With the majority of new cars supporting integration with Apple CarPlay (and/or Google’s offering, Android Auto), making the most of an in-car presence will become a must-have for many apps. Apple’s suite of core iOS apps Music, Maps, Calendar, Messages, Podcasts, News already support CarPlay, and are being joined by a growing host of third-party offerings. If you interested in learning more, CarPlay App Programming Guide provides a good introduction to CarPlay’s context, and introduces some of the latest templates to be added to the CarPlay platform.