From 59e51ab023904344a7dccbf77bb633e26a696fc0 Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Mon, 1 Dec 2025 19:07:09 +0200 Subject: [PATCH 1/3] fix: carplay support on example app for latest RN versions --- example/ios/SampleApp.xcodeproj/project.pbxproj | 8 ++++++++ example/ios/SampleApp/AppDelegateCarPlay.h | 3 --- example/ios/SampleApp/AppDelegateCarPlay.m | 17 +++++++++++++++++ example/ios/SampleApp/PhoneSceneDelegate.m | 13 +++++++++---- example/package.json | 1 + 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/example/ios/SampleApp.xcodeproj/project.pbxproj b/example/ios/SampleApp.xcodeproj/project.pbxproj index 775b9f9c..4db69024 100644 --- a/example/ios/SampleApp.xcodeproj/project.pbxproj +++ b/example/ios/SampleApp.xcodeproj/project.pbxproj @@ -643,6 +643,10 @@ ENABLE_BITCODE = NO; INFOPLIST_FILE = "SampleApp/Info-CarPlay.plist"; IPHONEOS_DEPLOYMENT_TARGET = 16.6; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "CARPLAY=1", + ); LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -672,6 +676,10 @@ CURRENT_PROJECT_VERSION = 1; INFOPLIST_FILE = SampleApp/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 16.6; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "CARPLAY=1", + ); LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/example/ios/SampleApp/AppDelegateCarPlay.h b/example/ios/SampleApp/AppDelegateCarPlay.h index e25833cd..29dbf073 100644 --- a/example/ios/SampleApp/AppDelegateCarPlay.h +++ b/example/ios/SampleApp/AppDelegateCarPlay.h @@ -17,7 +17,4 @@ #import @interface AppDelegateCarPlay : RCTAppDelegate -@property(nonatomic, strong) UIWindow *window; -@property(nonatomic, strong) RCTBridge *bridge; -@property(nonatomic, strong) RCTRootView *rootView; @end diff --git a/example/ios/SampleApp/AppDelegateCarPlay.m b/example/ios/SampleApp/AppDelegateCarPlay.m index 12d85f24..8a1b6cbd 100644 --- a/example/ios/SampleApp/AppDelegateCarPlay.m +++ b/example/ios/SampleApp/AppDelegateCarPlay.m @@ -30,6 +30,10 @@ - (BOOL)application:(UIApplication *)application self.moduleName = @"SampleApp"; self.dependencyProvider = [RCTAppDependencyProvider new]; + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = @{}; + // Note: Ensure that you have copied the Keys.plist.sample to Keys.plist // and have added the correct API_KEY value to the file. // @@ -39,6 +43,11 @@ - (BOOL)application:(UIApplication *)application NSString *api_key = [keysDictionary objectForKey:@"API_KEY"]; [GMSServices provideAPIKey:api_key]; + + // Set automaticallyLoadReactNativeWindow to NO to prevent RCTAppDelegate from creating the + // window. It will be created in PhoneSceneDelegate for the main (phone) screen. + self.automaticallyLoadReactNativeWindow = NO; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; } @@ -65,6 +74,14 @@ - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { } +- (BOOL)fabricEnabled { + return NO; +} + +- (BOOL)bridgelessEnabled { + return NO; +} + - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { return [self bundleURL]; } diff --git a/example/ios/SampleApp/PhoneSceneDelegate.m b/example/ios/SampleApp/PhoneSceneDelegate.m index 5e552d23..2d602fe9 100644 --- a/example/ios/SampleApp/PhoneSceneDelegate.m +++ b/example/ios/SampleApp/PhoneSceneDelegate.m @@ -14,17 +14,16 @@ * limitations under the License. */ #import "PhoneSceneDelegate.h" +#import #import #import -#import "AppDelegateCarPlay.h" @implementation PhoneSceneDelegate - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { - AppDelegateCarPlay *appDelegate = - (AppDelegateCarPlay *)[UIApplication sharedApplication].delegate; + RCTAppDelegate *appDelegate = (RCTAppDelegate *)[UIApplication sharedApplication].delegate; if (!appDelegate) { return; } @@ -34,12 +33,18 @@ - (void)scene:(UIScene *)scene return; } + UIView *rootView = [appDelegate.rootViewFactory viewWithModuleName:appDelegate.moduleName + initialProperties:appDelegate.initialProps + launchOptions:nil]; + rootView.backgroundColor = [UIColor whiteColor]; + UIViewController *rootViewController = [[UIViewController alloc] init]; - rootViewController.view = appDelegate.rootView; + rootViewController.view = rootView; UIWindow *window = [[UIWindow alloc] initWithWindowScene:windowScene]; window.rootViewController = rootViewController; self.window = window; + [appDelegate setWindow:window]; [window makeKeyAndVisible]; } diff --git a/example/package.json b/example/package.json index 43155671..f37bd4eb 100644 --- a/example/package.json +++ b/example/package.json @@ -7,6 +7,7 @@ "android": "react-native run-android", "android-release": "react-native run-android --mode release", "ios": "react-native run-ios", + "ios:carplay": "react-native run-ios --scheme SampleAppCarPlay", "ios-release": "react-native run-ios --mode Release", "lint": "eslint .", "test": "jest", From 01ecc6e272162e74469b254c830d5a2f2d89c72d Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Tue, 2 Dec 2025 08:35:22 +0200 Subject: [PATCH 2/3] docs: update documentation for CarPlay --- CARPLAY.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/CARPLAY.md b/CARPLAY.md index 8c36b91b..bd42a695 100644 --- a/CARPLAY.md +++ b/CARPLAY.md @@ -12,8 +12,10 @@ This guide explains how to enable and integrate Apple CarPlay with the React Nat Refer to the [Apple CarPlay Developer Guide](https://developer.apple.com/carplay/) to understand how CarPlay works and to complete the initial setup. Key steps include: -- Adding the CarPlay entitlement to your Xcode project. -- Creating a separate scene for the CarPlay map and enabling support for multiple scenes. +- Adding the CarPlay entitlement to your Xcode project +- Creating a separate scene for the CarPlay map and enabling support for multiple scenes + +For a complete implementation example, refer to [AppDelegateCarPlay.m](example/ios/SampleApp/AppDelegateCarPlay.m), [PhoneSceneDelegate.m](example/ios/SampleApp/PhoneSceneDelegate.m) and [CarSceneDelegate.m](example/ios/SampleApp/CarSceneDelegate.m). Pay special attention to how the React Native window is initialized in `PhoneSceneDelegate` when using multiple scenes. ### SceneDelegate for CarPlay @@ -83,4 +85,10 @@ For a more detailed example, refer to the `NavigationScreen.tsx` in the React Na ## Example Project -For a fully functional CarPlay implementation, check out the [SampleApp](./example/ios/) Xcode project, which includes the `SampleAppCarPlay` build target. The sample already contains test entitlement so you don't need to request one from Apple to run it. +For a fully functional CarPlay implementation, check out the [SampleApp](./example/ios/) Xcode project, which includes the `SampleAppCarPlay` build target. The sample already contains test entitlement so you don't need to request one from Apple to run it. + +Start the CarPlay example from the command line: + +```bash +yarn example ios:carplay +``` From c320f77862fb141d68f383750f2d5bec282b0cae Mon Sep 17 00:00:00 2001 From: Joonas Kerttula Date: Tue, 2 Dec 2025 08:58:34 +0200 Subject: [PATCH 3/3] fix: info.plist file for carplay target --- example/ios/SampleApp.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/ios/SampleApp.xcodeproj/project.pbxproj b/example/ios/SampleApp.xcodeproj/project.pbxproj index 4db69024..bee13b17 100644 --- a/example/ios/SampleApp.xcodeproj/project.pbxproj +++ b/example/ios/SampleApp.xcodeproj/project.pbxproj @@ -674,7 +674,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = SampleApp/SampleApp.entitlements; CURRENT_PROJECT_VERSION = 1; - INFOPLIST_FILE = SampleApp/Info.plist; + INFOPLIST_FILE = "SampleApp/Info-CarPlay.plist"; IPHONEOS_DEPLOYMENT_TARGET = 16.6; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)",