From 2a21b1c3d4a8a07b88f8e2cfc0604a58548e1494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Tue, 2 Jan 2024 10:25:32 +0100 Subject: [PATCH 1/5] feat: add Swift entrypoint [wip] add module maps to some RN modules to allow for swift c++ imports feat: implement RCTReactController and RCTSwiftUIAppDelegate feat: introduce new method to RCTAppDelegate --- .../Libraries/AppDelegate/RCTAppDelegate.h | 3 + .../Libraries/AppDelegate/RCTAppDelegate.mm | 91 +++++++++++-------- .../SwiftExtensions/RCTReactViewController.h | 11 +++ .../SwiftExtensions/RCTReactViewController.m | 31 +++++++ .../RCTRootViewRepresentable.swift | 29 ++++++ .../React-RCTSwiftExtensions.podspec | 25 +++++ .../React/CoreModules/RCTAppearance.mm | 3 +- .../react-native/scripts/react_native_pods.rb | 1 + packages/rn-tester/Podfile.lock | 4 + .../rn-tester/RNTester-visionOS/App.swift | 14 +++ .../RNTester-visionOS/AppDelegate.swift | 13 +++ .../RNTesterPods.xcodeproj/project.pbxproj | 18 ++-- 12 files changed, 192 insertions(+), 51 deletions(-) create mode 100644 packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.h create mode 100644 packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.m create mode 100644 packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift create mode 100644 packages/react-native/Libraries/SwiftExtensions/React-RCTSwiftExtensions.podspec create mode 100644 packages/rn-tester/RNTester-visionOS/App.swift create mode 100644 packages/rn-tester/RNTester-visionOS/AppDelegate.swift diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h index 4c89afce6d7814..f38b36e99e92d9 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h @@ -161,6 +161,9 @@ NS_ASSUME_NONNULL_BEGIN /// Return the bundle URL for the main bundle. - (NSURL *)bundleURL; +/// Don't use this method, it's going to be removed soon. +- (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary*)initialProperties launchOptions:(NSDictionary*)launchOptions; + @end NS_ASSUME_NONNULL_END diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index 969aff1a410ed2..cc27bfa138b3cf 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -83,51 +83,16 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( { RCTSetNewArchEnabled([self newArchEnabled]); BOOL enableTM = self.turboModuleEnabled; - BOOL fabricEnabled = self.fabricEnabled; - BOOL enableBridgeless = self.bridgelessEnabled; - - NSDictionary *initProps = updateInitialProps([self prepareInitialProps], fabricEnabled); RCTAppSetupPrepareApp(application, enableTM, *_reactNativeConfig); - - UIView *rootView; - if (enableBridgeless) { - // Enable native view config interop only if both bridgeless mode and Fabric is enabled. - RCTSetUseNativeViewConfigsInBridgelessMode(fabricEnabled); - - // Enable TurboModule interop by default in Bridgeless mode - RCTEnableTurboModuleInterop(YES); - RCTEnableTurboModuleInteropBridgeProxy(YES); - - [self createReactHost]; - [RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self; - RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:self.moduleName initialProperties:initProps]; - - RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc] - initWithSurface:surface - sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact]; - - rootView = (RCTRootView *)surfaceHostingProxyRootView; - } else { - if (!self.bridge) { - self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions]; - } - if ([self newArchEnabled]) { - self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge - contextContainer:_contextContainer]; - self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter; - - [RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self; - } - rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps]; - } - - [self customizeRootView:(RCTRootView *)rootView]; + #if TARGET_OS_VISION - self.window = [[UIWindow alloc] initWithFrame:RCTForegroundWindow().bounds]; + /// Bail out of UIWindow initializaiton to support multi-window scenarios in SwiftUI lifecycle. + return YES; #else + UIView* rootView = [self viewWithModuleName:self.moduleName initialProperties:[self prepareInitialProps] launchOptions:launchOptions]; + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; -#endif UIViewController *rootViewController = [self createRootViewController]; [self setRootView:rootView toRootViewController:rootViewController]; @@ -136,6 +101,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [self.window makeKeyAndVisible]; return YES; +#endif } - (void)applicationDidEnterBackground:(UIApplication *)application @@ -143,6 +109,51 @@ - (void)applicationDidEnterBackground:(UIApplication *)application // Noop } +- (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary*)initialProperties launchOptions:(NSDictionary*)launchOptions { + BOOL fabricEnabled = self.fabricEnabled; + BOOL enableBridgeless = self.bridgelessEnabled; + + NSDictionary *initProps = updateInitialProps(initialProperties, fabricEnabled); + + UIView *rootView; + if (enableBridgeless) { + // Enable native view config interop only if both bridgeless mode and Fabric is enabled. + RCTSetUseNativeViewConfigsInBridgelessMode(self.fabricEnabled); + + // Enable TurboModule interop by default in Bridgeless mode + RCTEnableTurboModuleInterop(YES); + RCTEnableTurboModuleInteropBridgeProxy(YES); + + [self createReactHost]; + [RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self; + RCTFabricSurface *surface = [_reactHost createSurfaceWithModuleName:self.moduleName initialProperties:initProps]; + + RCTSurfaceHostingProxyRootView *surfaceHostingProxyRootView = [[RCTSurfaceHostingProxyRootView alloc] + initWithSurface:surface + sizeMeasureMode:RCTSurfaceSizeMeasureModeWidthExact | RCTSurfaceSizeMeasureModeHeightExact]; + + rootView = (RCTRootView *)surfaceHostingProxyRootView; + } else { + if (!self.bridge) { + self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions]; + } + if ([self newArchEnabled]) { + if (!self.bridgeAdapter) { + self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge + contextContainer:_contextContainer]; + self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter; + + [RCTComponentViewFactory currentComponentViewFactory].thirdPartyFabricComponentsProvider = self; + } + } + rootView = [self createRootViewWithBridge:self.bridge moduleName:moduleName initProps:initProps]; + } + + [self customizeRootView:(RCTRootView *)rootView]; + + return rootView; +} + - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { [NSException raise:@"RCTBridgeDelegate::sourceURLForBridge not implemented" diff --git a/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.h b/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.h new file mode 100644 index 00000000000000..49f121175ef28d --- /dev/null +++ b/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.h @@ -0,0 +1,11 @@ +#import + +@interface RCTReactViewController : UIViewController + +@property (nonatomic, strong) NSString *_Nonnull moduleName; +@property (nonatomic, strong) NSDictionary *_Nullable initialProps; + +- (instancetype _Nonnull)initWithModuleName:(NSString *_Nonnull)moduleName + initProps:(NSDictionary *_Nullable)initProps; + +@end diff --git a/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.m b/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.m new file mode 100644 index 00000000000000..57492296c61d78 --- /dev/null +++ b/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.m @@ -0,0 +1,31 @@ +#import "RCTReactViewController.h" + +@protocol RCTRootViewFactoryProtocol + +- (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary*)initialProperties launchOptions:(NSDictionary*)launchOptions; + +@end + +@implementation RCTReactViewController + +- (instancetype)initWithModuleName:(NSString *)moduleName initProps:(NSDictionary *)initProps { + if (self = [super init]) { + _moduleName = moduleName; + _initialProps = initProps; + } + return self; +} + +// TODO: Temporary solution for creating RCTRootView on demand. This should be done through factory pattern, see here: https://github.com/facebook/react-native/pull/42263 +- (void)loadView { + id appDelegate = [UIApplication sharedApplication].delegate; + if ([appDelegate respondsToSelector:@selector(viewWithModuleName:initialProperties:launchOptions:)]) { + id delegate = (id)appDelegate; + self.view = [delegate viewWithModuleName:_moduleName initialProperties:_initialProps launchOptions:@{}]; + } else { + [NSException raise:@"UIApplicationDelegate:viewWithModuleName:initialProperties:launchOptions: not implemented" + format:@"Make sure you subclass RCTAppDelegate"]; + } +} + +@end diff --git a/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift b/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift new file mode 100644 index 00000000000000..9de42abf0d5317 --- /dev/null +++ b/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift @@ -0,0 +1,29 @@ +import SwiftUI + +/* + * Use this struct in SwiftUI context to present React Native rootView. + * + * Example: + * ``` + * WindowGroup { + * RCTRootViewRepresentable(moduleName: "RNTesterApp", initialProps: [:]) + * } + * ``` + */ +public struct RCTRootViewRepresentable: UIViewControllerRepresentable { + var moduleName: String + var initialProps: [AnyHashable: Any]? + + public init(moduleName: String, initialProps: [AnyHashable : Any]?) { + self.moduleName = moduleName + self.initialProps = initialProps + } + + public func makeUIViewController(context: Context) -> UIViewController { + RCTReactViewController(moduleName: moduleName, initProps: initialProps) + } + + public func updateUIViewController(_ uiViewController: UIViewController, context: Context) { + // noop + } +} diff --git a/packages/react-native/Libraries/SwiftExtensions/React-RCTSwiftExtensions.podspec b/packages/react-native/Libraries/SwiftExtensions/React-RCTSwiftExtensions.podspec new file mode 100644 index 00000000000000..3c603ddd6d8c74 --- /dev/null +++ b/packages/react-native/Libraries/SwiftExtensions/React-RCTSwiftExtensions.podspec @@ -0,0 +1,25 @@ +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json"))) +version = package['version'] + +source = { :git => 'https://github.com/facebook/react-native.git' } +if version == '1000.0.0' + # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") +else + source[:tag] = "v#{version}" +end + +Pod::Spec.new do |s| + s.name = "React-RCTSwiftExtensions" + s.version = version + s.summary = "A library for easier React Native integration with SwiftUI." + s.homepage = "https://reactnative.dev/" + s.license = package["license"] + s.author = "Callstack" + s.platforms = min_supported_versions + s.source = source + s.source_files = "*.{swift,h,m}" + s.frameworks = ["UIKit", "SwiftUI"] +end diff --git a/packages/react-native/React/CoreModules/RCTAppearance.mm b/packages/react-native/React/CoreModules/RCTAppearance.mm index 543bc1b791bead..5094097f5597c5 100644 --- a/packages/react-native/React/CoreModules/RCTAppearance.mm +++ b/packages/react-native/React/CoreModules/RCTAppearance.mm @@ -70,7 +70,8 @@ @implementation RCTAppearance { - (instancetype)init { if ((self = [super init])) { - UITraitCollection *traitCollection = RCTSharedApplication().delegate.window.traitCollection; + // TODO: Remove this after merging this PR upstream: https://github.com/facebook/react-native/pull/42231 + UITraitCollection *traitCollection = RCTKeyWindow().traitCollection; _currentColorScheme = RCTColorSchemePreference(traitCollection); [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appearanceChanged:) diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 810474d8d610d5..1ca7637dfe6630 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -132,6 +132,7 @@ def use_react_native! ( pod 'React-jserrorhandler', :path => "#{prefix}/ReactCommon/jserrorhandler" pod 'React-nativeconfig', :path => "#{prefix}/ReactCommon" pod 'RCTDeprecation', :path => "#{prefix}/ReactApple/Libraries/RCTFoundation/RCTDeprecation" + pod 'React-RCTSwiftExtensions', :path => "#{prefix}/Libraries/SwiftExtensions" if hermes_enabled setup_hermes!(:react_native_path => prefix) diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 794987a476e6f1..b325b26124933e 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -1069,6 +1069,7 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon + - React-RCTSwiftExtensions (1000.0.0) - React-RCTTest (1000.0.0): - RCT-Folly (= 2024.01.01.00) - React-Core (= 1000.0.0) @@ -1265,6 +1266,7 @@ DEPENDENCIES: - React-RCTNetwork (from `../react-native/Libraries/Network`) - React-RCTPushNotification (from `../react-native/Libraries/PushNotificationIOS`) - React-RCTSettings (from `../react-native/Libraries/Settings`) + - React-RCTSwiftExtensions (from `../react-native/Libraries/SwiftExtensions`) - React-RCTTest (from `./RCTTest`) - React-RCTText (from `../react-native/Libraries/Text`) - React-RCTVibration (from `../react-native/Libraries/Vibration`) @@ -1375,6 +1377,8 @@ EXTERNAL SOURCES: :path: "../react-native/Libraries/PushNotificationIOS" React-RCTSettings: :path: "../react-native/Libraries/Settings" + React-RCTSwiftExtensions: + :path: "../react-native/Libraries/SwiftExtensions" React-RCTTest: :path: "./RCTTest" React-RCTText: diff --git a/packages/rn-tester/RNTester-visionOS/App.swift b/packages/rn-tester/RNTester-visionOS/App.swift new file mode 100644 index 00000000000000..9c8766e9cfec43 --- /dev/null +++ b/packages/rn-tester/RNTester-visionOS/App.swift @@ -0,0 +1,14 @@ +import SwiftUI +import React +import React_RCTSwiftExtensions + +@main +struct RNTesterApp: App { + @UIApplicationDelegateAdaptor var delegate: AppDelegate + + var body: some Scene { + WindowGroup { + RCTRootViewRepresentable(moduleName: "RNTesterApp", initialProps: nil) + } + } +} diff --git a/packages/rn-tester/RNTester-visionOS/AppDelegate.swift b/packages/rn-tester/RNTester-visionOS/AppDelegate.swift new file mode 100644 index 00000000000000..cf164ab3877837 --- /dev/null +++ b/packages/rn-tester/RNTester-visionOS/AppDelegate.swift @@ -0,0 +1,13 @@ +import UIKit +import React +import React_RCTAppDelegate + +class AppDelegate: RCTAppDelegate { + override func sourceURL(for bridge: RCTBridge) -> URL? { + self.bundleURL() + } + + override func bundleURL() -> URL? { + RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "js/RNTesterApp.ios") + } +} diff --git a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj index f63f639044255a..03aa87e910f0b8 100644 --- a/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj +++ b/packages/rn-tester/RNTesterPods.xcodeproj/project.pbxproj @@ -15,11 +15,8 @@ 5C60EB1C226440DB0018C04F /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C60EB1B226440DB0018C04F /* AppDelegate.mm */; }; 763DC37D2B0F824400D2C0C5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 763DC37C2B0F824400D2C0C5 /* Assets.xcassets */; }; 763DC3802B0F824400D2C0C5 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 763DC37F2B0F824400D2C0C5 /* Preview Assets.xcassets */; }; - 763DC3862B0F839A00D2C0C5 /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5C60EB1B226440DB0018C04F /* AppDelegate.mm */; }; - 763DC3872B0F839A00D2C0C5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; - 763DC3882B0F839A00D2C0C5 /* SwiftTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832F45BA2A8A6E1F0097B4E6 /* SwiftTest.swift */; }; - 763DC38A2B0F83B500D2C0C5 /* FlexibleSizeExampleView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27F441E81BEBE5030039B79C /* FlexibleSizeExampleView.mm */; }; - 763DC38B2B0F83B500D2C0C5 /* UpdatePropertiesExampleView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 272E6B3C1BEA849E001FCF37 /* UpdatePropertiesExampleView.mm */; }; + 76E4BB282B34909800B02A15 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76E4BB272B34909800B02A15 /* App.swift */; }; + 76E4BB2C2B34932200B02A15 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76E4BB2B2B34932200B02A15 /* AppDelegate.swift */; }; 8145AE06241172D900A3F8DA /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8145AE05241172D900A3F8DA /* LaunchScreen.storyboard */; }; 832F45BB2A8A6E1F0097B4E6 /* SwiftTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 832F45BA2A8A6E1F0097B4E6 /* SwiftTest.swift */; }; CD10C7A5290BD4EB0033E1ED /* RCTEventEmitterTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CD10C7A4290BD4EB0033E1ED /* RCTEventEmitterTests.m */; }; @@ -108,6 +105,8 @@ 763DC37C2B0F824400D2C0C5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 763DC37F2B0F824400D2C0C5 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 763DC3812B0F824400D2C0C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 76E4BB272B34909800B02A15 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = ""; }; + 76E4BB2B2B34932200B02A15 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7CDA7A212644C6BB8C0D00D8 /* Pods-RNTesterIntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RNTesterIntegrationTests.release.xcconfig"; path = "Target Support Files/Pods-RNTesterIntegrationTests/Pods-RNTesterIntegrationTests.release.xcconfig"; sourceTree = ""; }; 8145AE05241172D900A3F8DA /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = RNTester/LaunchScreen.storyboard; sourceTree = ""; }; 832F45BA2A8A6E1F0097B4E6 /* SwiftTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SwiftTest.swift; path = RNTester/SwiftTest.swift; sourceTree = ""; }; @@ -299,6 +298,8 @@ 763DC3732B0F824200D2C0C5 /* RNTester-visionOS */ = { isa = PBXGroup; children = ( + 76E4BB2B2B34932200B02A15 /* AppDelegate.swift */, + 76E4BB272B34909800B02A15 /* App.swift */, 763DC37C2B0F824400D2C0C5 /* Assets.xcassets */, 763DC3812B0F824400D2C0C5 /* Info.plist */, 763DC37E2B0F824400D2C0C5 /* Preview Content */, @@ -885,11 +886,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 763DC38B2B0F83B500D2C0C5 /* UpdatePropertiesExampleView.mm in Sources */, - 763DC38A2B0F83B500D2C0C5 /* FlexibleSizeExampleView.mm in Sources */, - 763DC3882B0F839A00D2C0C5 /* SwiftTest.swift in Sources */, - 763DC3862B0F839A00D2C0C5 /* AppDelegate.mm in Sources */, - 763DC3872B0F839A00D2C0C5 /* main.m in Sources */, + 76E4BB2C2B34932200B02A15 /* AppDelegate.swift in Sources */, + 76E4BB282B34909800B02A15 /* App.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From d1c07bcd4e6210ea7adefe0a49edcaca377491a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Tue, 16 Jan 2024 13:43:34 +0100 Subject: [PATCH 2/5] feat: modify template to use SwiftUI --- .../HelloWorld.xcodeproj/project.pbxproj | 27 +++++------ .../template/visionos/HelloWorld/App.swift | 14 ++++++ .../visionos/HelloWorld/AppDelegate.h | 6 --- .../visionos/HelloWorld/AppDelegate.mm | 31 ------------ .../visionos/HelloWorld/AppDelegate.swift | 17 +++++++ .../template/visionos/HelloWorld/Info.plist | 2 - .../HelloWorld/LaunchScreen.storyboard | 47 ------------------- .../template/visionos/HelloWorld/main.m | 10 ---- 8 files changed, 42 insertions(+), 112 deletions(-) create mode 100644 packages/react-native/template/visionos/HelloWorld/App.swift delete mode 100644 packages/react-native/template/visionos/HelloWorld/AppDelegate.h delete mode 100644 packages/react-native/template/visionos/HelloWorld/AppDelegate.mm create mode 100644 packages/react-native/template/visionos/HelloWorld/AppDelegate.swift delete mode 100644 packages/react-native/template/visionos/HelloWorld/LaunchScreen.storyboard delete mode 100644 packages/react-native/template/visionos/HelloWorld/main.m diff --git a/packages/react-native/template/visionos/HelloWorld.xcodeproj/project.pbxproj b/packages/react-native/template/visionos/HelloWorld.xcodeproj/project.pbxproj index 038699e9316dba..715f4c231b42c1 100644 --- a/packages/react-native/template/visionos/HelloWorld.xcodeproj/project.pbxproj +++ b/packages/react-native/template/visionos/HelloWorld.xcodeproj/project.pbxproj @@ -9,11 +9,10 @@ /* Begin PBXBuildFile section */ 00E356F31AD99517003FC87E /* HelloWorldTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* HelloWorldTests.m */; }; 0C80B921A6F3F58F76C31292 /* libPods-HelloWorld.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-HelloWorld.a */; }; - 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 767CEB962B56C0F3000139AD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767CEB952B56C0F3000139AD /* AppDelegate.swift */; }; + 767CEB982B56C0FA000139AD /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767CEB972B56C0FA000139AD /* App.swift */; }; 7699B88040F8A987B510C191 /* libPods-HelloWorld-HelloWorldTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-HelloWorld-HelloWorldTests.a */; }; - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -31,17 +30,15 @@ 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 00E356F21AD99517003FC87E /* HelloWorldTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HelloWorldTests.m; sourceTree = ""; }; 13B07F961A680F5B00A75B9A /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = HelloWorld/AppDelegate.h; sourceTree = ""; }; - 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = HelloWorld/AppDelegate.mm; sourceTree = ""; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = HelloWorld/Images.xcassets; sourceTree = ""; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = HelloWorld/Info.plist; sourceTree = ""; }; - 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = HelloWorld/main.m; sourceTree = ""; }; 19F6CBCC0A4E27FBF8BF4A61 /* libPods-HelloWorld-HelloWorldTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld-HelloWorldTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 3B4392A12AC88292D35C810B /* Pods-HelloWorld.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.debug.xcconfig"; path = "Target Support Files/Pods-HelloWorld/Pods-HelloWorld.debug.xcconfig"; sourceTree = ""; }; 5709B34CF0A7D63546082F79 /* Pods-HelloWorld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.release.xcconfig"; path = "Target Support Files/Pods-HelloWorld/Pods-HelloWorld.release.xcconfig"; sourceTree = ""; }; 5B7EB9410499542E8C5724F5 /* Pods-HelloWorld-HelloWorldTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld-HelloWorldTests.debug.xcconfig"; path = "Target Support Files/Pods-HelloWorld-HelloWorldTests/Pods-HelloWorld-HelloWorldTests.debug.xcconfig"; sourceTree = ""; }; 5DCACB8F33CDC322A6C60F78 /* libPods-HelloWorld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = HelloWorld/LaunchScreen.storyboard; sourceTree = ""; }; + 767CEB952B56C0F3000139AD /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = VisionAppTestSUI/AppDelegate.swift; sourceTree = ""; }; + 767CEB972B56C0FA000139AD /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = App.swift; path = VisionAppTestSUI/App.swift; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-HelloWorld-HelloWorldTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld-HelloWorldTests.release.xcconfig"; path = "Target Support Files/Pods-HelloWorld-HelloWorldTests/Pods-HelloWorld-HelloWorldTests.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -86,12 +83,10 @@ 13B07FAE1A68108700A75B9A /* HelloWorld */ = { isa = PBXGroup; children = ( - 13B07FAF1A68108700A75B9A /* AppDelegate.h */, - 13B07FB01A68108700A75B9A /* AppDelegate.mm */, + 767CEB952B56C0F3000139AD /* AppDelegate.swift */, + 767CEB972B56C0FA000139AD /* App.swift */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, - 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, - 13B07FB71A68108700A75B9A /* main.m */, ); name = HelloWorld; sourceTree = ""; @@ -241,7 +236,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -392,8 +386,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, - 13B07FC11A68108700A75B9A /* main.m in Sources */, + 767CEB962B56C0F3000139AD /* AppDelegate.swift in Sources */, + 767CEB982B56C0FA000139AD /* App.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -484,9 +478,10 @@ SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "7"; + TARGETED_DEVICE_FAMILY = 7; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -516,7 +511,7 @@ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "7"; + TARGETED_DEVICE_FAMILY = 7; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/packages/react-native/template/visionos/HelloWorld/App.swift b/packages/react-native/template/visionos/HelloWorld/App.swift new file mode 100644 index 00000000000000..01f008b38ffa7c --- /dev/null +++ b/packages/react-native/template/visionos/HelloWorld/App.swift @@ -0,0 +1,14 @@ +import SwiftUI +import React +import React_RCTSwiftExtensions + +@main +struct HelloWorldApp: App { + @UIApplicationDelegateAdaptor var delegate: AppDelegate + + var body: some Scene { + WindowGroup { + RCTRootViewRepresentable(moduleName: "HelloWorld", initialProps: nil) + } + } +} diff --git a/packages/react-native/template/visionos/HelloWorld/AppDelegate.h b/packages/react-native/template/visionos/HelloWorld/AppDelegate.h deleted file mode 100644 index 5d2808256ca079..00000000000000 --- a/packages/react-native/template/visionos/HelloWorld/AppDelegate.h +++ /dev/null @@ -1,6 +0,0 @@ -#import -#import - -@interface AppDelegate : RCTAppDelegate - -@end diff --git a/packages/react-native/template/visionos/HelloWorld/AppDelegate.mm b/packages/react-native/template/visionos/HelloWorld/AppDelegate.mm deleted file mode 100644 index 0fd74b157dc2c6..00000000000000 --- a/packages/react-native/template/visionos/HelloWorld/AppDelegate.mm +++ /dev/null @@ -1,31 +0,0 @@ -#import "AppDelegate.h" - -#import - -@implementation AppDelegate - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - self.moduleName = @"HelloWorld"; - // 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 = @{}; - - return [super application:application didFinishLaunchingWithOptions:launchOptions]; -} - -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge -{ - return [self getBundleURL]; -} - -- (NSURL *)getBundleURL -{ -#if DEBUG - return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; -#else - return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; -#endif -} - -@end diff --git a/packages/react-native/template/visionos/HelloWorld/AppDelegate.swift b/packages/react-native/template/visionos/HelloWorld/AppDelegate.swift new file mode 100644 index 00000000000000..4e68fb4c1bd7ab --- /dev/null +++ b/packages/react-native/template/visionos/HelloWorld/AppDelegate.swift @@ -0,0 +1,17 @@ +import UIKit +import React +import React_RCTAppDelegate + +class AppDelegate: RCTAppDelegate { + override func sourceURL(for bridge: RCTBridge) -> URL? { + self.bundleURL() + } + + override func bundleURL() -> URL? { +#if DEBUG + RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") +#else + Bundle.main.url(forResource: "main", withExtension: "jsbundle") +#endif + } +} diff --git a/packages/react-native/template/visionos/HelloWorld/Info.plist b/packages/react-native/template/visionos/HelloWorld/Info.plist index c004615da4e278..b7a713898f10ae 100644 --- a/packages/react-native/template/visionos/HelloWorld/Info.plist +++ b/packages/react-native/template/visionos/HelloWorld/Info.plist @@ -34,8 +34,6 @@ NSLocationWhenInUseUsageDescription - UILaunchStoryboardName - LaunchScreen UIRequiredDeviceCapabilities armv7 diff --git a/packages/react-native/template/visionos/HelloWorld/LaunchScreen.storyboard b/packages/react-native/template/visionos/HelloWorld/LaunchScreen.storyboard deleted file mode 100644 index e13962e9bf5470..00000000000000 --- a/packages/react-native/template/visionos/HelloWorld/LaunchScreen.storyboard +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/packages/react-native/template/visionos/HelloWorld/main.m b/packages/react-native/template/visionos/HelloWorld/main.m deleted file mode 100644 index d645c7246c42e4..00000000000000 --- a/packages/react-native/template/visionos/HelloWorld/main.m +++ /dev/null @@ -1,10 +0,0 @@ -#import - -#import "AppDelegate.h" - -int main(int argc, char *argv[]) -{ - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} From 1f0dbbbecd40500fae3758e73e65a0ae7ee23a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Wed, 17 Jan 2024 16:06:38 +0100 Subject: [PATCH 3/5] fix: dimensions, use RCTMainWindow() --- .../Libraries/AppDelegate/RCTAppDelegate.h | 2 +- .../Libraries/AppDelegate/RCTAppDelegate.mm | 3 + .../SwiftExtensions/RCTMainWindow.swift | 34 ++++++ .../SwiftExtensions/RCTReactViewController.h | 9 +- .../SwiftExtensions/RCTReactViewController.m | 5 + .../RCTRootViewRepresentable.swift | 27 +++-- .../React-RCTSwiftExtensions.podspec | 2 + .../React/Base/RCTBridgeDelegate.h | 2 +- packages/react-native/template/_gitignore | 1 + .../HelloWorld.xcodeproj/project.pbxproj | 16 +-- .../template/visionos/HelloWorld/App.swift | 4 +- .../visionos/HelloWorld/AppDelegate.swift | 2 +- packages/rn-tester/Podfile.lock | 107 +++++++++--------- .../rn-tester/RNTester-visionOS/App.swift | 4 +- .../RNTester-visionOS/AppDelegate.swift | 2 +- 15 files changed, 135 insertions(+), 85 deletions(-) create mode 100644 packages/react-native/Libraries/SwiftExtensions/RCTMainWindow.swift diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h index f38b36e99e92d9..00e27912ea19f5 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.h @@ -159,7 +159,7 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)bridgelessEnabled; /// Return the bundle URL for the main bundle. -- (NSURL *)bundleURL; +- (NSURL *__nullable)bundleURL; /// Don't use this method, it's going to be removed soon. - (UIView *)viewWithModuleName:(NSString *)moduleName initialProperties:(NSDictionary*)initialProperties launchOptions:(NSDictionary*)launchOptions; diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index cc27bfa138b3cf..d8a69464c2c67a 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -312,6 +312,9 @@ - (Class)getModuleClassFromName:(const char *)name - (void)createReactHost { + if (_reactHost != nil) { + return; + } __weak __typeof(self) weakSelf = self; _reactHost = [[RCTHost alloc] initWithBundleURL:[self bundleURL] hostDelegate:nil diff --git a/packages/react-native/Libraries/SwiftExtensions/RCTMainWindow.swift b/packages/react-native/Libraries/SwiftExtensions/RCTMainWindow.swift new file mode 100644 index 00000000000000..1bf0bca5901b63 --- /dev/null +++ b/packages/react-native/Libraries/SwiftExtensions/RCTMainWindow.swift @@ -0,0 +1,34 @@ +import SwiftUI + +/** + This SwiftUI struct returns main React Native scene. It should be used only once as it conains setup code. + + Example: + ```swift + @main + struct YourApp: App { + @UIApplicationDelegateAdaptor var delegate: AppDelegate + + var body: some Scene { + RCTMainWindow(moduleName: "YourApp") + } + } + ``` + + Note: If you want to create additional windows in your app, create a new `WindowGroup {}` and pass it a `RCTRootViewRepresentable`. +*/ +public struct RCTMainWindow: Scene { + var moduleName: String + var initialProps: RCTRootViewRepresentable.InitialPropsType + + public init(moduleName: String, initialProps: RCTRootViewRepresentable.InitialPropsType = nil) { + self.moduleName = moduleName + self.initialProps = initialProps + } + + public var body: some Scene { + WindowGroup { + RCTRootViewRepresentable(moduleName: moduleName, initialProps: initialProps) + } + } +} diff --git a/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.h b/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.h index 49f121175ef28d..13de19c37cf0e2 100644 --- a/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.h +++ b/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.h @@ -1,9 +1,14 @@ #import +/** + A `UIViewController` responsible for embeding `RCTRootView` inside. Uses Factory pattern to retrive new view instances. + + Note: Used to in `RCTRootViewRepresentable` to display React views. + */ @interface RCTReactViewController : UIViewController -@property (nonatomic, strong) NSString *_Nonnull moduleName; -@property (nonatomic, strong) NSDictionary *_Nullable initialProps; +@property (nonatomic, strong, nonnull) NSString *moduleName; +@property (nonatomic, strong, nullable) NSDictionary *initialProps; - (instancetype _Nonnull)initWithModuleName:(NSString *_Nonnull)moduleName initProps:(NSDictionary *_Nullable)initProps; diff --git a/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.m b/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.m index 57492296c61d78..4f0fd95a55bce7 100644 --- a/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.m +++ b/packages/react-native/Libraries/SwiftExtensions/RCTReactViewController.m @@ -1,4 +1,5 @@ #import "RCTReactViewController.h" +#import @protocol RCTRootViewFactoryProtocol @@ -16,6 +17,10 @@ - (instancetype)initWithModuleName:(NSString *)moduleName initProps:(NSDictionar return self; } +- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { + [[NSNotificationCenter defaultCenter] postNotificationName:RCTWindowFrameDidChangeNotification object:self]; +} + // TODO: Temporary solution for creating RCTRootView on demand. This should be done through factory pattern, see here: https://github.com/facebook/react-native/pull/42263 - (void)loadView { id appDelegate = [UIApplication sharedApplication].delegate; diff --git a/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift b/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift index 9de42abf0d5317..1d9441975bf81c 100644 --- a/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift +++ b/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift @@ -1,20 +1,23 @@ import SwiftUI -/* - * Use this struct in SwiftUI context to present React Native rootView. - * - * Example: - * ``` - * WindowGroup { - * RCTRootViewRepresentable(moduleName: "RNTesterApp", initialProps: [:]) - * } - * ``` - */ +/** + SwiftUI view enclosing `RCTReactViewController`. Its main purpose is to display React Native views inside of SwiftUI lifecycle. + + Use it create new windows in your app: + Example: + ```swift + WindowGroup { + RCTRootViewRepresentable(moduleName: "YourAppName", initialProps: [:]) + } + ``` +*/ public struct RCTRootViewRepresentable: UIViewControllerRepresentable { + public typealias InitialPropsType = [AnyHashable: Any]? + var moduleName: String - var initialProps: [AnyHashable: Any]? + var initialProps: InitialPropsType - public init(moduleName: String, initialProps: [AnyHashable : Any]?) { + public init(moduleName: String, initialProps: InitialPropsType = nil) { self.moduleName = moduleName self.initialProps = initialProps } diff --git a/packages/react-native/Libraries/SwiftExtensions/React-RCTSwiftExtensions.podspec b/packages/react-native/Libraries/SwiftExtensions/React-RCTSwiftExtensions.podspec index 3c603ddd6d8c74..d5dbdafe37ff6d 100644 --- a/packages/react-native/Libraries/SwiftExtensions/React-RCTSwiftExtensions.podspec +++ b/packages/react-native/Libraries/SwiftExtensions/React-RCTSwiftExtensions.podspec @@ -22,4 +22,6 @@ Pod::Spec.new do |s| s.source = source s.source_files = "*.{swift,h,m}" s.frameworks = ["UIKit", "SwiftUI"] + + s.dependency "React-Core" end diff --git a/packages/react-native/React/Base/RCTBridgeDelegate.h b/packages/react-native/React/Base/RCTBridgeDelegate.h index 0d9ec0a2b137bc..1bc3723ea9ed12 100644 --- a/packages/react-native/React/Base/RCTBridgeDelegate.h +++ b/packages/react-native/React/Base/RCTBridgeDelegate.h @@ -20,7 +20,7 @@ NS_ASSUME_NONNULL_BEGIN * When running from a locally bundled JS file, this should be a `file://` url * pointing to a path inside the app resources, e.g. `file://.../main.jsbundle`. */ -- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge; +- (NSURL *__nullable)sourceURLForBridge:(RCTBridge *)bridge; @optional diff --git a/packages/react-native/template/_gitignore b/packages/react-native/template/_gitignore index b0dcf472880ef0..794c9344167271 100644 --- a/packages/react-native/template/_gitignore +++ b/packages/react-native/template/_gitignore @@ -65,3 +65,4 @@ yarn-error.log # testing /coverage +.yarn diff --git a/packages/react-native/template/visionos/HelloWorld.xcodeproj/project.pbxproj b/packages/react-native/template/visionos/HelloWorld.xcodeproj/project.pbxproj index 715f4c231b42c1..41d39acfc43ca3 100644 --- a/packages/react-native/template/visionos/HelloWorld.xcodeproj/project.pbxproj +++ b/packages/react-native/template/visionos/HelloWorld.xcodeproj/project.pbxproj @@ -10,8 +10,8 @@ 00E356F31AD99517003FC87E /* HelloWorldTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* HelloWorldTests.m */; }; 0C80B921A6F3F58F76C31292 /* libPods-HelloWorld.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-HelloWorld.a */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 767CEB962B56C0F3000139AD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767CEB952B56C0F3000139AD /* AppDelegate.swift */; }; - 767CEB982B56C0FA000139AD /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767CEB972B56C0FA000139AD /* App.swift */; }; + 767CEBBC2B582F6B000139AD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767CEBBB2B582F6B000139AD /* AppDelegate.swift */; }; + 767CEBBE2B582F78000139AD /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 767CEBBD2B582F78000139AD /* App.swift */; }; 7699B88040F8A987B510C191 /* libPods-HelloWorld-HelloWorldTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-HelloWorld-HelloWorldTests.a */; }; /* End PBXBuildFile section */ @@ -37,8 +37,8 @@ 5709B34CF0A7D63546082F79 /* Pods-HelloWorld.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld.release.xcconfig"; path = "Target Support Files/Pods-HelloWorld/Pods-HelloWorld.release.xcconfig"; sourceTree = ""; }; 5B7EB9410499542E8C5724F5 /* Pods-HelloWorld-HelloWorldTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld-HelloWorldTests.debug.xcconfig"; path = "Target Support Files/Pods-HelloWorld-HelloWorldTests/Pods-HelloWorld-HelloWorldTests.debug.xcconfig"; sourceTree = ""; }; 5DCACB8F33CDC322A6C60F78 /* libPods-HelloWorld.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-HelloWorld.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 767CEB952B56C0F3000139AD /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = VisionAppTestSUI/AppDelegate.swift; sourceTree = ""; }; - 767CEB972B56C0FA000139AD /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = App.swift; path = VisionAppTestSUI/App.swift; sourceTree = ""; }; + 767CEBBB2B582F6B000139AD /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = HelloWorld/AppDelegate.swift; sourceTree = ""; }; + 767CEBBD2B582F78000139AD /* App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = App.swift; path = HelloWorld/App.swift; sourceTree = ""; }; 89C6BE57DB24E9ADA2F236DE /* Pods-HelloWorld-HelloWorldTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HelloWorld-HelloWorldTests.release.xcconfig"; path = "Target Support Files/Pods-HelloWorld-HelloWorldTests/Pods-HelloWorld-HelloWorldTests.release.xcconfig"; sourceTree = ""; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ @@ -83,8 +83,8 @@ 13B07FAE1A68108700A75B9A /* HelloWorld */ = { isa = PBXGroup; children = ( - 767CEB952B56C0F3000139AD /* AppDelegate.swift */, - 767CEB972B56C0FA000139AD /* App.swift */, + 767CEBBB2B582F6B000139AD /* AppDelegate.swift */, + 767CEBBD2B582F78000139AD /* App.swift */, 13B07FB51A68108700A75B9A /* Images.xcassets */, 13B07FB61A68108700A75B9A /* Info.plist */, ); @@ -386,8 +386,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 767CEB962B56C0F3000139AD /* AppDelegate.swift in Sources */, - 767CEB982B56C0FA000139AD /* App.swift in Sources */, + 767CEBBC2B582F6B000139AD /* AppDelegate.swift in Sources */, + 767CEBBE2B582F78000139AD /* App.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/react-native/template/visionos/HelloWorld/App.swift b/packages/react-native/template/visionos/HelloWorld/App.swift index 01f008b38ffa7c..c1d601b2201ba9 100644 --- a/packages/react-native/template/visionos/HelloWorld/App.swift +++ b/packages/react-native/template/visionos/HelloWorld/App.swift @@ -7,8 +7,6 @@ struct HelloWorldApp: App { @UIApplicationDelegateAdaptor var delegate: AppDelegate var body: some Scene { - WindowGroup { - RCTRootViewRepresentable(moduleName: "HelloWorld", initialProps: nil) - } + RCTMainWindow(moduleName: "HelloWorld") } } diff --git a/packages/react-native/template/visionos/HelloWorld/AppDelegate.swift b/packages/react-native/template/visionos/HelloWorld/AppDelegate.swift index 4e68fb4c1bd7ab..1583449a06a0ae 100644 --- a/packages/react-native/template/visionos/HelloWorld/AppDelegate.swift +++ b/packages/react-native/template/visionos/HelloWorld/AppDelegate.swift @@ -9,7 +9,7 @@ class AppDelegate: RCTAppDelegate { override func bundleURL() -> URL? { #if DEBUG - RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") + RCTBundleURLProvider.sharedSettings()?.jsBundleURL(forBundleRoot: "index") #else Bundle.main.url(forResource: "main", withExtension: "jsbundle") #endif diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index b325b26124933e..946b0f8a795e4e 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -1069,7 +1069,8 @@ PODS: - React-jsi - React-NativeModulesApple - ReactCommon - - React-RCTSwiftExtensions (1000.0.0) + - React-RCTSwiftExtensions (1000.0.0): + - React-Core - React-RCTTest (1000.0.0): - RCT-Folly (= 2024.01.01.00) - React-Core (= 1000.0.0) @@ -1191,7 +1192,6 @@ PODS: - RCT-Folly (= 2024.01.01.00) - React-callinvoker (= 1000.0.0) - React-cxxreact (= 1000.0.0) - - React-debug (= 1000.0.0) - React-jsi (= 1000.0.0) - React-logger (= 1000.0.0) - React-perflogger (= 1000.0.0) @@ -1420,65 +1420,66 @@ CHECKOUT OPTIONS: SPEC CHECKSUMS: boost: 8f1e9b214fa11f71081fc8ecd5fad3daf221cf7f DoubleConversion: 71bf0761505a44e4dfddc0aa04afa049fdfb63b5 - FBLazyVector: bdac74ca2911225376312021896e15c79dbf9fcb + FBLazyVector: e34ddc4948ab5a9f857262cf68a46d39d8c61ed1 fmt: 5d9ffa7ccba126c08b730252123601d514652320 glog: 4f05d17aa39a829fee878689fc9a41af587fabba hermes-engine: 3fed7e58e811ae8f795063cc6450714395c0276d - MyNativeView: f3d15118880cbd9b2a619c620b4709bf69fa61d7 - NativeCxxModuleExample: c078c9bc1d22c81572c993ce2c1e9e8fbd8ae2a8 + MyNativeView: 534e99e9c5dfd0bae242bdb06bb72e11d720c9a2 + NativeCxxModuleExample: 107af3af8f5ce8802037937aabf1872ac891ad43 OCMock: 267d92c078398b7ce11d99e811e3a402744c06bc RCT-Folly: 70c792c856324d6a518af75b3a307c14c226343a RCTDeprecation: 3808e36294137f9ee5668f4df2e73dc079cd1dcf - RCTRequired: 6aeca084db3de8b52fd8bb5cc836fe7d7b2b5751 - RCTTypeSafety: 43fcd6846d491ddf9babbe4952c17f2ce6ea5666 - React: b4e48c185ef18e24a9b037cfb9f4078dc6355d7c - React-callinvoker: 02d5475486a607fffcfd83b258731aa46bb06727 + RCTRequired: 60c97b0b0cc33a209d33b3412eb1a218cbc1b82c + RCTTypeSafety: 2f597b431b910be1e49c6e6f40dd6879f6d1bdfd + React: dc827cd6966c06176ea9664cb4d74d63d1a4fcca + React-callinvoker: 3727946c98d5626aef39ac6a45b9d1be5db35527 React-Codegen: 04a9ac6fd2f392534ed432102c6cbf117dcbf62b - React-Core: f8004b0136f7decc8279532634b28938612d6f85 - React-CoreModules: 9c3294cce3e00032144028032acf4da22d56b924 - React-cxxreact: 73fd9a8dde65edd4a1c6cfad2d7431a57bf60f94 - React-debug: 32dab6cb8b0d3432232c45dea7457ae5238c47cf - React-Fabric: bc8cbcd1404edb57493d52002fadb6bc1f417e8f - React-FabricImage: 48bc60132fb10a0eeae72d72dd81943c16872ede - React-graphics: c930135abad3098e4b82dad3f298feae52829f80 - React-hermes: 0a8c4bfcbdc1ff61b9d1993363aeda6802c3815c - React-ImageManager: 88eac6de7cff39d2ba2cdb06bd98bd1c4629399d - React-jserrorhandler: c01a0b7135825b753fd5c729b2deb290f2480ea3 - React-jsi: 9382a899fb5b5f84f4546b50cca64778635c4f82 - React-jsiexecutor: cb27970e14d32627b627894e701a9ae5dae55054 - React-jsinspector: 060e667e014a572852aadcc816550200942920ea - React-jsitracing: c4cbdb7c48c65729f3c4ba4dbc0d94135818d882 - React-logger: b0a5a630737a8161629c1d29cc37249b03d7177d - React-Mapbuffer: bc62c7a8db875f429288640af4a75708a6631e77 - React-nativeconfig: d70bae868914e174f305250afbaa9f17ece24ca2 - React-NativeModulesApple: 17857f8e70713707dc6699e44a60604e340ca26a - React-perflogger: b4cf18ad5a647180372b0017976f0785dd58e37c - React-RCTActionSheet: 67e887502a6b109a840dc086caa365ef38b697ae - React-RCTAnimation: 55b4c85ef5508f395c981e5a5364579d1eb0e8ac - React-RCTAppDelegate: ad500d624c40190f44408a87ae850da050e83383 - React-RCTBlob: 3cd3d166d9263c40b1f09423d595a72ee5f6ba03 - React-RCTFabric: ef602234a80209e28f4a4b04cc42b81f286b4cf1 - React-RCTImage: c8b1f97471711a64c54103aaf3abb06ffe4e9367 - React-RCTLinking: 39f9a96c680014d7864e2a94328c9b5c16b1bc2f - React-RCTNetwork: c153dc34eb3b4243dafeb7732a7004794f2fcfc2 - React-RCTPushNotification: 7e8b4717f9f49b15e30bf2440988f0c8dad34b9c - React-RCTSettings: 4389617d10fce8fa1e59623f07830ae4d16accb4 - React-RCTTest: baa5056754918b7f42adfda1db81105ed05fa990 - React-RCTText: 88d7180f8a88c29cb9c783f6c671375cc261923b - React-RCTVibration: 672865035f44075ae945c37ac0508930dd5979b4 - React-rendererdebug: eaf5fe3f507afa26e683c9618fd717aea88065eb - React-rncore: 113f8658923af62e2306c5c52b591eee101625a6 - React-RuntimeApple: a17be9669a016c7cda7889e9b5ee7489afcd627a - React-RuntimeCore: 615ab6fc54247e7243013530c8526121bbed70e8 - React-runtimeexecutor: ebc5eec4b653cca71a8974f209a04e241f453e39 - React-RuntimeHermes: 032fba7487b5949524489cd7ed218f3d1bd8de58 - React-runtimescheduler: 763397cd4ded78ab6d5f0f563ce97622fdc108df - React-utils: 596fb6f15c61d94aab31500e2886e7b7d5b5c416 - ReactCommon: 754052eef88f1ceb0b0b709cd0872d91c32bb270 - ReactCommon-Samples: 37ea01aea69b7fa25eeaf37a77e644629fda4ea4 - ScreenshotManager: abb77feb92964d59f57e0b112797fe24bd25aea6 + React-Core: eb5002d75c263aaa042b97853effd3b6e2f8668c + React-CoreModules: 1eb22960e980f9e0b80542da464c5935260fd180 + React-cxxreact: eb5eb55bffe0ccff4cbf192a04c978e37f137e8f + React-debug: 1d5a869d405ca2410bfc95c7fe340220331696ef + React-Fabric: 555fc115322e9b37b8cc1d4ac0ab8d4a87718077 + React-FabricImage: 657d05951f1a512496713d7c2d534d347915fe3b + React-graphics: 989577b97b4d5e8f3cd3ff86b275eb7e4c7ff657 + React-hermes: 0003c9f0f623ed87224f4415c567f90f0d15737f + React-ImageManager: ec80ccfd373893be8c347a23a693438b41a86e2a + React-jserrorhandler: c4126c1c2fb42540cf64fdc80917214a0c6ca905 + React-jsi: 81558ed18c4e91824280a3298d75d598ef4cf3fa + React-jsiexecutor: 139ad70390e61c6b00779d82c7d5f490cd3eb632 + React-jsinspector: bd27899ffc61660559bfbc8872e8d8de05b2612d + React-jsitracing: 9639d0659449fa9a71f31e28d7dde349bcec3394 + React-logger: 142bc8479d40940294428f652fb016b809335121 + React-Mapbuffer: 2762ea21725665466ea54f09848886f5ba7e97f7 + React-nativeconfig: f984aee5151a69d520ea830756b33c62acc5c269 + React-NativeModulesApple: 5fd38cf3bd4aca4adc9522a7a53681001f98f29d + React-perflogger: b19adeb816c255a04dff9528cf3f6bf209188d17 + React-RCTActionSheet: c07c3924b6a94602f40effc130b776faa8b6bab1 + React-RCTAnimation: 51f5a6be1479336b26d0cb282bd0e8f1feabf593 + React-RCTAppDelegate: 3cac28ed63e10086624a2d965ce2c3359bbe04b0 + React-RCTBlob: 0d6f17d4cacc18b6872b06b232186b176d4a8a18 + React-RCTFabric: 8b955856519621a30a8425ac6c594abb66788839 + React-RCTImage: c69ac72c257475c7860602ac11beaabd7862b99c + React-RCTLinking: c3f99ac575655569f5bc108ff13b816dcdf1dbfd + React-RCTNetwork: 39405b5687d5b16fdcc6466713930e71f8389cde + React-RCTPushNotification: 8b6b936f8e07096db581a446d4f9844ff8b35925 + React-RCTSettings: 96dad99a283d67c573b8ba3709725901e8ac3108 + React-RCTSwiftExtensions: b372ca0daf79796645c691841b470ad5d37bba64 + React-RCTTest: 11be8ba66b91bc914d31f2b7c9abd0c39d47a3d7 + React-RCTText: 8ca2156c782dc33ceb3fa80ce8c20cfcccbcc9d1 + React-RCTVibration: 284a9575d09b5c5517d5d5b8b86d4e2d3c324b5e + React-rendererdebug: d664023e12af482bb3911ce547c522ddbbf01ac5 + React-rncore: 5917d667ddd77e1938a9d1dabf5c503e427f9ec9 + React-RuntimeApple: 31c6a97b53e53ea3e0d071ca45471b9152d22dc3 + React-RuntimeCore: 25febc097f3f7d4043562fcf5c7bf406758ad1ce + React-runtimeexecutor: b66459278d0c6564b5d877107e0bb3ac953a8221 + React-RuntimeHermes: 3f46517238fed32a04b36c6bc41a4ac8508baf6e + React-runtimescheduler: 04123aa94f0fa33398b9e58cd4345cac230eaee7 + React-utils: bdfeb70a54b7b73533de6c8b679143f75c34c86a + ReactCommon: 8f6a345ebd9558892a83e1be6b83bdad4fd99078 + ReactCommon-Samples: 090dcc2659d35c5577955d89efac618595a72e61 + ScreenshotManager: a5f596498de38f3cf3377df636ca67355503f190 SocketRocket: 0ba3e799f983d2dfa878777017659ef6c866e5c6 - Yoga: 49f2e65de656c1814c5151e72723dd4f76ff8163 + Yoga: ac56a983bdf421a579c197143f79aa568f1c74a1 PODFILE CHECKSUM: 7e999b8158f1055609ef4491bc35f1ad658fdd6c diff --git a/packages/rn-tester/RNTester-visionOS/App.swift b/packages/rn-tester/RNTester-visionOS/App.swift index 9c8766e9cfec43..39224b803cce1b 100644 --- a/packages/rn-tester/RNTester-visionOS/App.swift +++ b/packages/rn-tester/RNTester-visionOS/App.swift @@ -7,8 +7,6 @@ struct RNTesterApp: App { @UIApplicationDelegateAdaptor var delegate: AppDelegate var body: some Scene { - WindowGroup { - RCTRootViewRepresentable(moduleName: "RNTesterApp", initialProps: nil) - } + RCTMainWindow(moduleName: "RNTesterApp") } } diff --git a/packages/rn-tester/RNTester-visionOS/AppDelegate.swift b/packages/rn-tester/RNTester-visionOS/AppDelegate.swift index cf164ab3877837..0852368cd4101f 100644 --- a/packages/rn-tester/RNTester-visionOS/AppDelegate.swift +++ b/packages/rn-tester/RNTester-visionOS/AppDelegate.swift @@ -8,6 +8,6 @@ class AppDelegate: RCTAppDelegate { } override func bundleURL() -> URL? { - RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "js/RNTesterApp.ios") + RCTBundleURLProvider.sharedSettings()?.jsBundleURL(forBundleRoot: "js/RNTesterApp.ios") } } From cf11a721bb5af7aedce1ace938b7b70eac742da9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Tue, 23 Jan 2024 09:23:28 +0100 Subject: [PATCH 4/5] fix: fallback to DarkMode on visionOS --- .../Libraries/SwiftExtensions/RCTRootViewRepresentable.swift | 2 +- packages/react-native/React/CoreModules/RCTAppearance.mm | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift b/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift index 1d9441975bf81c..6a89db9d08e2d7 100644 --- a/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift +++ b/packages/react-native/Libraries/SwiftExtensions/RCTRootViewRepresentable.swift @@ -7,7 +7,7 @@ import SwiftUI Example: ```swift WindowGroup { - RCTRootViewRepresentable(moduleName: "YourAppName", initialProps: [:]) + RCTRootViewRepresentable(moduleName: "YourAppName") } ``` */ diff --git a/packages/react-native/React/CoreModules/RCTAppearance.mm b/packages/react-native/React/CoreModules/RCTAppearance.mm index 5094097f5597c5..e006f415d21efa 100644 --- a/packages/react-native/React/CoreModules/RCTAppearance.mm +++ b/packages/react-native/React/CoreModules/RCTAppearance.mm @@ -56,8 +56,8 @@ void RCTOverrideAppearancePreference(NSString *const colorSchemeOverride) // Return the default if the app doesn't allow different color schemes. return RCTAppearanceColorSchemeLight; } - - return appearances[@(traitCollection.userInterfaceStyle)] ?: RCTAppearanceColorSchemeLight; + // Fallback to dark mode on visionOS + return appearances[@(traitCollection.userInterfaceStyle)] ?: RCTAppearanceColorSchemeDark; } @interface RCTAppearance () From 0fcfeafb7a5084dfbb5324c0b6679d44e31318b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oskar=20Kwas=CC=81niewski?= Date: Tue, 23 Jan 2024 10:31:12 +0100 Subject: [PATCH 5/5] fix: use KeyWindow() in RCTPerfMonitor --- packages/react-native/React/CoreModules/RCTPerfMonitor.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native/React/CoreModules/RCTPerfMonitor.mm b/packages/react-native/React/CoreModules/RCTPerfMonitor.mm index d588c594bea0cb..faee93ca234190 100644 --- a/packages/react-native/React/CoreModules/RCTPerfMonitor.mm +++ b/packages/react-native/React/CoreModules/RCTPerfMonitor.mm @@ -315,7 +315,7 @@ - (void)show [self updateStats]; - UIWindow *window = RCTSharedApplication().delegate.window; + UIWindow *window = RCTKeyWindow(); [window addSubview:self.container]; _uiDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(threadUpdate:)];