What is CarPlay?

CarPlay is an Apple iOS car integration standard that allows you to display content from your iPhone onto your compatible car head unit and control your phone. Common uses for this include casting music from services such as Spotify or Apple Music or for trip navigation using a map application.

As CarPlay advances with each iOS release, more and more app categories are added, opening the door for more third-party apps to be created and accessed on your car head unit. Recently added categories include fast-food ordering apps and EV charging discovery apps which means that CarPlay is becoming more accessible than ever and is being added to an increasing number of apps.

CarPlay Development

How does CarPlay work?

CarPlay provides a series of templates the developer can use to display application data. These are highly opinionated and ensure that the provider user interface is safe for use within a car. The availability of these templates varies depending on the type of application you are developing. For example, a navigation app can utilize a large array of templates including map controls, lists, and grids. In contrast, a fast-food ordering application is restricted to using a smaller subset of templates. For highly specialized application types such as EV Charging providers, a special Point of Interest template is provided, which displays multiple points on both a map and a list simultaneously for you.

CarPlay and React Native

Using CarPlay with React Native is a pretty new concept but there is a package available on npm called react-native-carplay which is making great progress in this area. The React Native part of this library is written in Typescript, which provides appropriate typings for use within your application. The package allows you to create most of the supported templates and control the CarPlay display stack via pushing and popping templates to and from the display. Further to this, it provides hooks for CarPlay connection/disconnection, allowing you to react accordingly within your application.

Getting setup with this package requires a little bit of native code, but once this is done, everything should be done in the React Native side from then on. You can find full instructions for these steps in the package’s readme. For this reason, react-native-carplay is not supported in a managed expo environment at this time as this setup does not allow you to edit any of the native code yourself without first ejecting it from the managed workflow.

When wishing to publish a CarPlay application to the app store, you must have requested the appropriate entitlements from Apple. However, during development, you can use the built-in CarPlay simulator.  The latest iOS 14 simulator appears to be fully featured and acts almost identically to a physical CarPlay unit.

Integrate CarPlay into your React Native Application

When adding CarPlay to an existing React Native application, you really want to keep the phone and CarPlay screens in sync. This will allow you to hand off between the two when connecting/disconnecting from the CarPlay system. For example, if you’ve searched for a song on your phone and then connected to CarPlay, you would want CarPlay to start with your selected song on the screen. For this reason, it is best to create the appropriate CarPlay Template for your screen in the same location as the paired phone screen code within a useEffect or similar.

Adding a template

Let’s start by adding a simple CarPlay menu to a React Native application. When working with the CarPlay display stack, you must always have a root template. This is the base template that sits at the bottom of the display stack and usually takes the form of a GridTemplate or a MapTemplate depending on the type of application. A Grid can display up to 8 buttons along with bar buttons at the top of the screen for quick links to settings / volume / voice control etc.

import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import { CarPlay, GridTemplate } from 'react-native-carplay';

export function Index() {
  useEffect(() => {
    const gridTemplate = new GridTemplate({
      buttons: [
        {
          id: item0,
          titleVariants: ['Item 0],
          image: require('images/button.png'),
        },
        {
          id: 'item1',
          titleVariants: ['Item 1],
          image: require('images/button.png'),
        }
  // ...
      ],
      title: 'Grid Template',
    });

    CarPlay.setRootTemplate(gridTemplate);
  }, []);

  return (
    <View>
		<Text>Hello, world</Text>
    </View>
  );
}

Here you can see that we have created a new GridTemplate and called CarPlay.setRootTemplate within a useEffect. This ensures that this code is called only once and does not recreate the CarPlay template on every re-render of the react component because there are no dependencies passed to the useEffect

Handling connect / disconnect

When working with CarPlay, we only want to render templates when we’re connected, otherwise we could easily cause an error. react-native-carplay allows us to register a connection callback on the CarPlay class to signal a connection and render our Template at that time. We can add the connection state as a dependency to the useEffect in order to ensure it runs when the connection state changes.

import React, { useEffect, useState } from 'react';
import { View, Text } from 'react-native';
import { CarPlay, GridTemplate } from 'react-native-carplay';

export function Index() {

// default the carplayConnected value to the connected state of the CarPlay class
	const [carPlayConnected, setCarPlayConnected] = useState(CarPlay.connected);

  useEffect(() => {
    function onConnect() {
      setCarPlayConnected(true);
    }

    function onDisconnect() {
      setCarPlayConnected(false);
    }
	
// register connect and disconnect callbacks
    CarPlay.registerOnConnect(onConnect);
    CarPlay.registerOnDisconnect(onDisconnect);

    return () => {
// unregister the callbacks in the return statement
      CarPlay.unregisterOnConnect(onConnect);
      CarPlay.unregisterOnDisconnect(onDisconnect);
    };
  });

  useEffect(() => {
	// only create the template if connected
	  if (carPlayConnected) {
		const gridTemplate = new GridTemplate({
			buttons: [
			  {
				id: 'List',
				titleVariants: ['List'],
				image: require('images/button.png'),
			  },
			  {
				id: 'Grid',
				titleVariants: ['Grid'],
				image: require('images/button.png'),
			  }
			],
			title: 'Hello, world',
		  });
	  
		  CarPlay.setRootTemplate(gridTemplate);
		
		// clean up the grid template in the return statement
// this will only be run when carplayconnected changes
// to false, or the component is unmounted
		return () => {
	gridTemplate = undefined;
}
	  }
    
  }, [carPlayConnected]);

  return (
    <View>
		<Text>Hello, world</Text>
    </View>
  );
}

In the above example we’ve used both the connected state of the CarPlay class and the registerOnConnect function to ensure our grid template is created and torn down when connected and subsequently disconnected from a CarPlay display. Adding carPlayConnected as a dependency of the second useEffect ensures that the function runs whenever the value changes. In more complicated examples, you may need to store a reference to the template you have created between renders using the useRef hook. In this case, the return statement of your useEffect is even more important as this is where you would remove your references and perform any necessary clean up operations.

Updating templates

Some templates such as ListTemplate allow you to update them via an api once they have been created. For example, a list template can have its items updated via an updateSections function where sections are grouped arrays of items. The below example shows how you could update list items when new search results become available. It makes use of the useRef hook to ensure that the same List Template is used in every render.

import React, { useEffect, useRef, useCallback } from 'react';
import { View } from 'react-native';
import { CarPlay, ListTemplate } from 'react-native-carplay';

export function Index() {
	const listTemplate = useRef<ListTemplate>();

	useEffect(() => {
		listTemplate.current = new ListTemplate({
			title: 'Results',
			sections: []
		  });
	
		  CarPlay.pushTemplate(listTemplate.current);

		  return () => {
			  listTemplate.current = undefined;
		  }
	  }, []);

	  const onResults = useCallback((results) => {
		listTemplate.current.updateSections(results);
	  }, [listTemplate]);

  return (
    <View>
		<Search onResults={onResults}/>
    </View>
  );
}

As you can see in the above example, the onResults callback is calling the updateSections function on the listTemplate which is in turn displaying those new list items on the CarPlay screen. Creating a ref to the List Template and calling the update function in this way means that it will not have to recreate an entirely new template on each change and will provide the user with a better experience. More information about what is possible with each template can be found in the Apple CarPlay documentation. At this point, not all functionality is fully implemented in the react-native-carplay package but it is getting better over time and we at SitePen are actively contributing to its development.

Conclusion

Adding CarPlay to your application will help it to stand out against other applications without CarPlay support. It is being fitted by more and more car manufacturers and has been quicker to market with third-party API access than its main rival for android based phones Android Auto. Apple is adding more functionality and application types to CarPlay with each release making it a better time than ever to consider adding support to your application. 

Until recently, adding CarPlay support to a react native application would have meant writing a great deal of native code yourself but the react-native-carplay package solves this problem for you and provides you with a type-safe library to use in your React Native application. A more complete application example can be found in the /example directory of the react-native-carplay GitHub repository.