I had a hell of a time with this, but finally got it working. First, add a UIApplicationSceneManifest
to your plist. Note that UIApplicationSupportsMultipleScenes
need not be true. It's really intended for macOS/Catalyst apps that support multiple windows. But a CarPlay scene isn't another window
in this sense.
<plist>
<dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>CPTemplateApplicationSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>CPTemplateApplicationScene</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).CarPlaySceneDelegate</string>
<key>UISceneConfigurationName</key>
<string>CarPlay Configuration</string>
</dict>
</array>
</dict>
</dict>
</dict>
</plist>
Then your CarPlay scene delegate looks like this (note the class name matches the value of UISceneDelegateClassName
from your plist)
import CarPlay
class CarPlaySceneDelegate: UIResponder, CPTemplateApplicationSceneDelegate {
var interfaceController: CPInterfaceController?
func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene,
didConnect interfaceController: CPInterfaceController) {
self.interfaceController = interfaceController
let carPlayUI = ... // CPTabBarTemplate, CPListTemplate, CPGridTemplate, etc
interfaceController.setRootTemplate(carPlayUI, animated: true) { success, error in
// optional completion handler once CarPlay UI is displayed
}
}
func templateApplicationScene(_ templateApplicationScene: CPTemplateApplicationScene,
didDisconnect interfaceController: CPInterfaceController,
from window: CPWindow) {
self.interfaceController = nil
}
}