Upcoming and OnDemand Webinars View full list

Detecting `carAudio`

Chris Downie

How can you detect when the user connects their phone to their car’s stereo? This simple question that came up during a feature brainstorm session proved to have a surprisingly complicated answer. Let’s dive in.

Background on AVAudioSession

Audio on iOS is mostly handled by the OS itself. You communicate your intent to play audio by telling an instance of AVAudioSession (usually its singleton sharedInstance()).

Most important for this discussion, are the available audio routes on the shared AVAudioSession instance. Each audio route is either an input that’s receiving audio (like the phone’s microphone) or an output where we’re sending audio (like the phone’s speaker or a bluetooth headset). We can access the shared AVAudioSession’s current route, and inspect all of its outputs:

let audioSession = AVAudioSession.sharedInstance()
audioSession.currentRoute.outputs.forEach { output in
    print(output.portName)
}

Each output is of type AVAudioSessionPortDescription. Conveniently, in addition to each port having a name, it also has a type that’s one of these AVAudioSession.Port values. Among them is a convenient value called carAudio.

So! This should be easy! Monitor for changes to the ports in the shared audio session, and when we see one of type .carAudio, then we’re connected to a car!

Not So Fast

Turns out, there are a lot of ways to connect your iPhone to your car’s stereo:

  • A lightning-to-USB cable directly to the stereo system
  • The lightning-to-aux adapter
  • Direct bluetooth connection to the car
  • Bluetooth connection to an after-market stereo adapter
  • Bluetooth connection to a CarPlay-enabled car

Do these all report as .carAudio? Can we distinguish connecting to a car via bluetooth from connecting to AirPods? Can we distinguish between a lighting connection to EarPods and a lightning connection to a car?

To answer these questions, we built a little test app and passed it around the office. The app is pretty simple:

  1. Listen for any changes to the audio route by registering for AVAudioSession.routeChangeNotifications
  2. When a change occurs, record the reason for the change
  3. After each change, look at all the route’s output port descriptions, and record their name & type.

You can experiment with the app yourself by downloading it here.

The Results

Here’s a quick table of our results, showing the type of connection, if it’s actually a connection to a car, and the port’s portType value:

Connection Car? Port Type
After-market bluetooth-to-aux device Yes .bluetoothA2DP
Bose Bluetooth headphones No .bluetoothA2DP
AirPods No .bluetoothA2DP
Direct connection to car’s bluetooth Yes .bluetoothA2DP
Lightning-to-headphone adapter + aux Yes .headphones
Lightning EarPods No .headphones
iPhone to Car’s USB port Yes .usbAudio
Screen mirror to Apple TV No .airPlay
Bluetooth to a CarPlay-enabled car Yes .carAudio

We finally saw a .carAudio value, but only when directly connected to a CarPlay-enabled car. Even cars with native bluetooth support reported the connection as .bluetoothA2DP — just like any other bluetooth speaker.

However, these bluetooth connections do provide useful data. Each .bluetoothA2DP connection also provides an identifiable name and a unique ID. Rather than automatically connect to all cars with bluetooth support, we discovered it would be possible for the user to identify a specific bluetooth device (such as their car) they’re already connected to and the app would know whenever the user connects to that device again. That will have to do for now.

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project