diff --git a/.gitignore b/.gitignore index 8533677fca9d..2dbadbcccf4e 100644 --- a/.gitignore +++ b/.gitignore @@ -133,6 +133,7 @@ vendor/ # Swift Package build folder /packages/react-native/.build /packages/react-native/.swiftpm +/packages/react-native/React/includes/ # @react-native/codegen /packages/react-native/React/FBReactNativeSpec/ diff --git a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm index 8abc2fc431bf..db646544014b 100644 --- a/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm +++ b/packages/react-native/Libraries/AppDelegate/RCTAppDelegate.mm @@ -16,6 +16,7 @@ #import #import "RCTAppSetupUtils.h" #import "RCTDependencyProvider.h" +#import #if RN_DISABLE_OSS_PLUGIN_HEADER #import diff --git a/packages/react-native/Libraries/Package.swift b/packages/react-native/Libraries/Package.swift new file mode 100644 index 000000000000..9b7bab8be854 --- /dev/null +++ b/packages/react-native/Libraries/Package.swift @@ -0,0 +1,39 @@ +// swift-tools-version: 6.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "React-Libraries", + platforms: [.iOS(.v15), .macCatalyst(SupportedPlatform.MacCatalystVersion.v13)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "PushNotification", + targets: ["PushNotification"]), + ], + dependencies: [ + .package(name: "React", path: "../") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "PushNotification", + dependencies: ["React"], + path: "PushNotificationIOS", + exclude: ["React-RCTPushNotification.podspec", "NativePushNotificationManagerIOS.js", "PushNotificationIOS.d.ts", "PushNotificationIOS.js"], + publicHeadersPath: ".", + // cSettings: [ + // .headerSearchPath("headers") + // ], + cxxSettings: [ + // .headerSearchPath("headers"), + .unsafeFlags(["-std=c++20"]), + ], + linkerSettings: [ + .linkedFramework("Foundation") + ] + ), + ] +) diff --git a/packages/react-native/Package.swift b/packages/react-native/Package.swift index 4617284315d0..7ff21fc08e45 100644 --- a/packages/react-native/Package.swift +++ b/packages/react-native/Package.swift @@ -7,6 +7,9 @@ */ import PackageDescription +import Foundation + +let BUILD_FROM_SOURCE = false /** This is the `Package.swift` file that allows to build React Native core using Swift PM. @@ -33,6 +36,8 @@ let RuntimeExecutorPath = "ReactCommon/runtimeexecutor" // header only target let CallInvokerPath = "ReactCommon/callinvoker" // header only target let ReactFBReactNativeSpecPath = "React/FBReactNativeSpec" // generated let FBLazyVectorPath = "Libraries/FBLazyVector" // header only +let virtualViewPath = "ReactCommon/react/renderer/components/virtualview" // header only +let virtualViewExperimentalPath = "ReactCommon/react/renderer/components/virtualviewexperimental" // header only // MARK: Prebuilt Dependencies declaration let reactNativeDependencies = BinaryTarget( @@ -373,6 +378,8 @@ let reactRuntimeApple = RNTarget( dependencies: [.reactNativeDependencies, .jsi, .reactPerfLogger, .reactCxxReact, .rctDeprecation, .yoga, .reactRuntime, .reactRCTFabric, .reactCoreModules, .reactTurboModuleCore, .hermesPrebuilt, .reactUtils] ) +let publicHeadersPathForReactCore: String = BUILD_FROM_SOURCE ? "includes" : "." + /// React-Core.podspec let reactCore = RNTarget( name: .reactCore, @@ -385,7 +392,8 @@ let reactCore = RNTarget( linkedFrameworks: ["CoreServices"], excludedPaths: ["Fabric", "Tests", "Resources", "Runtime/RCTJscInstanceFactory.mm", "I18n/strings", "CxxBridge/JSCExecutorFactory.mm", "CoreModules"], dependencies: [.reactNativeDependencies, .reactCxxReact, .reactPerfLogger, .jsi, .reactJsiExecutor, .reactUtils, .reactFeatureFlags, .reactRuntimeScheduler, .yoga, .reactJsInspector, .reactJsiTooling, .rctDeprecation, .reactCoreRCTWebsocket, .reactRCTImage, .reactTurboModuleCore, .reactRCTText, .reactRCTBlob, .reactRCTAnimation, .reactRCTNetwork, .reactFabric, .hermesPrebuilt], - sources: [".", "Runtime/RCTHermesInstanceFactory.mm"] + sources: [".", "Runtime/RCTHermesInstanceFactory.mm"], + publicHeadersPath: publicHeadersPathForReactCore ) /// React-Fabric.podspec @@ -416,6 +424,7 @@ let reactFabric = RNTarget( "components/modal", "components/rncore", "components/safeareaview", + "components/switch", "components/text", "components/textinput", "components/textinput/platform/ios/", @@ -428,37 +437,68 @@ let reactFabric = RNTarget( sources: ["animations", "attributedstring", "core", "componentregistry", "componentregistry/native", "components/root", "components/view", "components/view/platform/cxx", "components/scrollview", "components/scrollview/platform/cxx", "components/legacyviewmanagerinterop", "dom", "scheduler", "mounting", "observers/events", "telemetry", "consistency", "leakchecker", "uimanager", "uimanager/consistency"] ) -/// React-RCTFabric.podspec -let reactRCTFabric = RNTarget( - name: .reactRCTFabric, - path: "React/Fabric", - dependencies: [.reactNativeDependencies, .reactCore, .reactRCTImage, .yoga, .reactRCTText, .jsi, .reactFabricComponents, .reactGraphics, .reactImageManager, .reactDebug, .reactUtils, .reactPerformanceTimeline, .reactRendererDebug, .reactRendererConsistency, .reactRuntimeScheduler, .reactRCTAnimation, .reactJsInspector, .reactJsInspectorNetwork, .reactJsInspectorTracing, .reactFabric, .reactFabricImage] +let reactFabricInputAccessory = RNTarget( + name: .reactFabricInputAccessory, + path: "ReactCommon/react/renderer/components/inputaccessory", + dependencies: [.reactNativeDependencies, .reactCore, .reactJsiExecutor, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .yoga, .reactRendererDebug, .reactGraphics, .reactFabric, .reactTurboModuleBridging] ) -/// React-FabricComponents.podspec -let reactFabricComponents = RNTarget( - name: .reactFabricComponents, - path: "ReactCommon/react/renderer", +let reactFabricModal = RNTarget( + name: .reactFabricModal, + path: "ReactCommon/react/renderer/components/modal", excludedPaths: [ - "components/modal/platform/android", - "components/modal/platform/cxx", - "components/view/platform/android", - "components/view/platform/windows", - "components/view/platform/macos", - "components/switch/iosswitch/react/renderer/components/switch/MacOSSwitchShadowNode.mm", - "components/textinput/platform/android", - "components/text/platform/android", - "components/textinput/platform/macos", - "components/text/tests", - "textlayoutmanager/tests", - "textlayoutmanager/platform/android", - "textlayoutmanager/platform/cxx", - "textlayoutmanager/platform/windows", - "textlayoutmanager/platform/macos", - "conponents/rncore", // this was the old folder where RN Core Components were generated. If you ran codegen in the past, you might have some files in it that might make the build fail. + "platform/android", + "platform/cxx", + ], + dependencies: [.reactNativeDependencies, .reactCore, .reactJsiExecutor, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .yoga, .reactRendererDebug, .reactGraphics, .reactFabric, .reactTurboModuleBridging] +) + +let reactFabricSwitch = RNTarget( + name: .reactFabricSwitch, + path: "ReactCommon/react/renderer/components/switch/iosswitch", + excludedPaths: ["react/renderer/components/switch/MacOSSwitchShadowNode.mm"], + dependencies: [.reactNativeDependencies, .reactCore, .reactJsiExecutor, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .yoga, .reactRendererDebug, .reactGraphics, .reactFabric, .reactTurboModuleBridging] +) + +let reactFabricSafeAreaView = RNTarget( + name: .reactFabricSafeAreaView, + path: "ReactCommon/react/renderer/components/safeareaview", + dependencies: [.reactNativeDependencies, .reactCore, .reactJsiExecutor, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .yoga, .reactRendererDebug, .reactGraphics, .reactFabric, .reactTurboModuleBridging] +) + +let reactFabricTextLayoutManager = RNTarget( + name: .reactFabricTextLayoutManager, + path: "ReactCommon/react/renderer/textlayoutmanager", + excludedPaths: [ + "platform/android", + "platform/cxx", + "platform/windows", + "platform/macos", + "tests", ], dependencies: [.reactNativeDependencies, .reactCore, .reactJsiExecutor, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .yoga, .reactRendererDebug, .reactGraphics, .reactFabric, .reactTurboModuleBridging], - sources: ["components/inputaccessory", "components/modal", "components/safeareaview", "components/text", "components/text/platform/cxx", "components/textinput", "components/textinput/platform/ios/", "components/unimplementedview", "components/virtualview", "components/virtualviewexperimental", "textlayoutmanager", "textlayoutmanager/platform/ios", "components/switch/iosswitch"] + sources: [".", "platform/ios"] +) + +let reactFabricText = RNTarget( + name: .reactFabricText, + path: "ReactCommon/react/renderer/components/text", + excludedPaths: [ + "platform/android", + "tests", + ], + dependencies: [.reactNativeDependencies, .reactCore, .reactJsiExecutor, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .yoga, .reactRendererDebug, .reactGraphics, .reactFabric, .reactTurboModuleBridging, .reactFabricTextLayoutManager], + sources: [".", "platform/cxx"] +) + +let reactFabricTextInput = RNTarget( + name: .reactFabricTextInput, + path: "ReactCommon/react/renderer/components/textinput", + excludedPaths: [ + "platform/android", + ], + dependencies: [.reactNativeDependencies, .reactCore, .reactJsiExecutor, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .yoga, .reactRendererDebug, .reactGraphics, .reactFabric, .reactTurboModuleBridging, .reactFabricTextLayoutManager], + sources: [".", "platform/ios"] ) /// React-FabricImage.podspec @@ -469,6 +509,19 @@ let reactFabricImage = RNTarget( dependencies: [.reactNativeDependencies, .reactFabric, .reactCore, .reactJsiExecutor, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .yoga, .reactRendererDebug, .reactGraphics, .reactTurboModuleBridging, .reactImageManagerApple] ) +let reactFabricUnimplementedView = RNTarget( + name: .reactFabricUnimplementedView, + path: "ReactCommon/react/renderer/components/unimplementedview", + dependencies: [.reactNativeDependencies, .reactCore, .reactJsiExecutor, .reactTurboModuleCore, .jsi, .logger, .reactDebug, .reactFeatureFlags, .reactUtils, .reactRuntimeScheduler, .reactCxxReact, .yoga, .reactRendererDebug, .reactGraphics, .reactFabric, .reactTurboModuleBridging] +) + +/// React-RCTFabric.podspec +let reactRCTFabric = RNTarget( + name: .reactRCTFabric, + path: "React/Fabric", + dependencies: [.reactNativeDependencies, .reactCore, .reactRCTImage, .yoga, .reactRCTText, .jsi, .reactFabricInputAccessory, .reactFabricModal, .reactFabricSafeAreaView, .reactFabricSwitch, .reactFabricText, .reactFabricTextInput, .reactFabricUnimplementedView, .reactFabricTextLayoutManager, .reactGraphics, .reactImageManager, .reactDebug, .reactUtils, .reactPerformanceTimeline, .reactRendererDebug, .reactRendererConsistency, .reactRuntimeScheduler, .reactRCTAnimation, .reactJsInspector, .reactJsInspectorNetwork, .reactJsInspectorTracing, .reactFabric, .reactFabricImage] +) + /// React-ImageManagerApple.podspec let reactImageManagerApple = RNTarget( name: .reactImageManagerApple, @@ -573,8 +626,15 @@ let targets = [ reactCoreRCTWebsocket, reactFabric, reactRCTFabric, - reactFabricComponents, reactFabricImage, + reactFabricInputAccessory, + reactFabricModal, + reactFabricSafeAreaView, + reactFabricSwitch, + reactFabricTextLayoutManager, + reactFabricText, + reactFabricTextInput, + reactFabricUnimplementedView, reactNativeDependencies, hermesPrebuilt, reactJsiTooling, @@ -738,12 +798,20 @@ extension String { static let reactJsInspector = "React-jsinspector" static let reactJsInspectorTracing = "React-jsinspectortracing" static let reactCxxReact = "React-cxxreact" - static let reactCore = "React-Core" - static let reactCoreRCTWebsocket = "React-Core/RCTWebSocket" + static let reactCore = "React" + static let reactCoreRCTWebsocket = "React/RCTWebSocket" static let reactFabric = "React-Fabric" static let reactRCTFabric = "React-RCTFabric" - static let reactFabricComponents = "React-FabricComponents" + static let reactFabricImage = "React-FabricImage" + static let reactFabricInputAccessory = "React-FabricInputAccessory" + static let reactFabricModal = "React-FabricModal" + static let reactFabricSafeAreaView = "React-FabricSafeAreaView" + static let reactFabricSwitch = "React-FabricSwitch" + static let reactFabricTextLayoutManager = "React-FabricTextLayoutManager" + static let reactFabricText = "React-FabricText" + static let reactFabricTextInput = "React-FabricTextInput" + static let reactFabricUnimplementedView = "React-FabricUnimplementedView" static let reactNativeDependencies = "ReactNativeDependencies" diff --git a/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap b/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap index 1f04a84d2400..8c76f937ae04 100644 --- a/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap +++ b/packages/react-native/scripts/codegen/__tests__/__snapshots__/generate-artifacts-executor-test.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`execute test-app "RCTAppDependencyProvider.h" should match snapshot 1`] = ` +exports[`execute test-app "ReactAppDependencyProvider/RCTAppDependencyProvider.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -29,7 +29,7 @@ NS_ASSUME_NONNULL_END " `; -exports[`execute test-app "RCTAppDependencyProvider.mm" should match snapshot 1`] = ` +exports[`execute test-app "ReactAppDependencyProvider/RCTAppDependencyProvider.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -73,7 +73,45 @@ exports[`execute test-app "RCTAppDependencyProvider.mm" should match snapshot 1` " `; -exports[`execute test-app "RCTModuleProviders.h" should match snapshot 1`] = ` +exports[`execute test-app "ReactAppDependencyProvider/ReactAppDependencyProvider.podspec" should match snapshot 1`] = ` +"# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +version = \\"1000.0.0\\" +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 = \\"ReactAppDependencyProvider\\" + s.version = version + s.summary = \\"The third party dependency provider for the app\\" + s.homepage = \\"https://reactnative.dev/\\" + s.documentation_url = \\"https://reactnative.dev/\\" + s.license = \\"MIT\\" + s.author = \\"Meta Platforms, Inc. and its affiliates\\" + s.platforms = min_supported_versions + s.source = source + s.source_files = \\"**/RCTAppDependencyProvider.{h,mm}\\" + + # This guard prevent to install the dependencies when we run \`pod install\` in the old architecture. + s.pod_target_xcconfig = { + \\"CLANG_CXX_LANGUAGE_STANDARD\\" => rct_cxx_language_standard(), + \\"DEFINES_MODULE\\" => \\"YES\\" + } + + s.dependency \\"ReactCodegen\\" +end +" +`; + +exports[`execute test-app "ReactCodegen/RCTModuleProviders.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -93,7 +131,7 @@ exports[`execute test-app "RCTModuleProviders.h" should match snapshot 1`] = ` " `; -exports[`execute test-app "RCTModuleProviders.mm" should match snapshot 1`] = ` +exports[`execute test-app "ReactCodegen/RCTModuleProviders.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -159,7 +197,7 @@ exports[`execute test-app "RCTModuleProviders.mm" should match snapshot 1`] = ` " `; -exports[`execute test-app "RCTModulesConformingToProtocolsProvider.h" should match snapshot 1`] = ` +exports[`execute test-app "ReactCodegen/RCTModulesConformingToProtocolsProvider.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -181,7 +219,7 @@ exports[`execute test-app "RCTModulesConformingToProtocolsProvider.h" should mat " `; -exports[`execute test-app "RCTModulesConformingToProtocolsProvider.mm" should match snapshot 1`] = ` +exports[`execute test-app "ReactCodegen/RCTModulesConformingToProtocolsProvider.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -248,7 +286,7 @@ exports[`execute test-app "RCTModulesConformingToProtocolsProvider.mm" should ma " `; -exports[`execute test-app "RCTThirdPartyComponentsProvider.h" should match snapshot 1`] = ` +exports[`execute test-app "ReactCodegen/RCTThirdPartyComponentsProvider.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -268,7 +306,7 @@ exports[`execute test-app "RCTThirdPartyComponentsProvider.h" should match snaps " `; -exports[`execute test-app "RCTThirdPartyComponentsProvider.mm" should match snapshot 1`] = ` +exports[`execute test-app "ReactCodegen/RCTThirdPartyComponentsProvider.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -303,7 +341,7 @@ exports[`execute test-app "RCTThirdPartyComponentsProvider.mm" should match snap " `; -exports[`execute test-app "RCTUnstableModulesRequiringMainQueueSetupProvider.h" should match snapshot 1`] = ` +exports[`execute test-app "ReactCodegen/RCTUnstableModulesRequiringMainQueueSetupProvider.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -321,7 +359,7 @@ exports[`execute test-app "RCTUnstableModulesRequiringMainQueueSetupProvider.h" " `; -exports[`execute test-app "RCTUnstableModulesRequiringMainQueueSetupProvider.mm" should match snapshot 1`] = ` +exports[`execute test-app "ReactCodegen/RCTUnstableModulesRequiringMainQueueSetupProvider.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -355,45 +393,7 @@ exports[`execute test-app "RCTUnstableModulesRequiringMainQueueSetupProvider.mm" " `; -exports[`execute test-app "ReactAppDependencyProvider.podspec" should match snapshot 1`] = ` -"# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -version = \\"1000.0.0\\" -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 = \\"ReactAppDependencyProvider\\" - s.version = version - s.summary = \\"The third party dependency provider for the app\\" - s.homepage = \\"https://reactnative.dev/\\" - s.documentation_url = \\"https://reactnative.dev/\\" - s.license = \\"MIT\\" - s.author = \\"Meta Platforms, Inc. and its affiliates\\" - s.platforms = min_supported_versions - s.source = source - s.source_files = \\"**/RCTAppDependencyProvider.{h,mm}\\" - - # This guard prevent to install the dependencies when we run \`pod install\` in the old architecture. - s.pod_target_xcconfig = { - \\"CLANG_CXX_LANGUAGE_STANDARD\\" => rct_cxx_language_standard(), - \\"DEFINES_MODULE\\" => \\"YES\\" - } - - s.dependency \\"ReactCodegen\\" -end -" -`; - -exports[`execute test-app "ReactCodegen.podspec" should match snapshot 1`] = ` +exports[`execute test-app "ReactCodegen/ReactCodegen.podspec" should match snapshot 1`] = ` "# Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under the MIT license found in the @@ -507,7 +507,7 @@ end " `; -exports[`execute test-app-legacy "RCTAppDependencyProvider.h" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactAppDependencyProvider/RCTAppDependencyProvider.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -536,7 +536,7 @@ NS_ASSUME_NONNULL_END " `; -exports[`execute test-app-legacy "RCTAppDependencyProvider.mm" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactAppDependencyProvider/RCTAppDependencyProvider.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -580,7 +580,45 @@ exports[`execute test-app-legacy "RCTAppDependencyProvider.mm" should match snap " `; -exports[`execute test-app-legacy "RCTModuleProviders.h" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactAppDependencyProvider/ReactAppDependencyProvider.podspec" should match snapshot 1`] = ` +"# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +version = \\"1000.0.0\\" +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 = \\"ReactAppDependencyProvider\\" + s.version = version + s.summary = \\"The third party dependency provider for the app\\" + s.homepage = \\"https://reactnative.dev/\\" + s.documentation_url = \\"https://reactnative.dev/\\" + s.license = \\"MIT\\" + s.author = \\"Meta Platforms, Inc. and its affiliates\\" + s.platforms = min_supported_versions + s.source = source + s.source_files = \\"**/RCTAppDependencyProvider.{h,mm}\\" + + # This guard prevent to install the dependencies when we run \`pod install\` in the old architecture. + s.pod_target_xcconfig = { + \\"CLANG_CXX_LANGUAGE_STANDARD\\" => rct_cxx_language_standard(), + \\"DEFINES_MODULE\\" => \\"YES\\" + } + + s.dependency \\"ReactCodegen\\" +end +" +`; + +exports[`execute test-app-legacy "ReactCodegen/RCTModuleProviders.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -600,7 +638,7 @@ exports[`execute test-app-legacy "RCTModuleProviders.h" should match snapshot 1` " `; -exports[`execute test-app-legacy "RCTModuleProviders.mm" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactCodegen/RCTModuleProviders.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -657,7 +695,7 @@ exports[`execute test-app-legacy "RCTModuleProviders.mm" should match snapshot 1 " `; -exports[`execute test-app-legacy "RCTModulesConformingToProtocolsProvider.h" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactCodegen/RCTModulesConformingToProtocolsProvider.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -679,7 +717,7 @@ exports[`execute test-app-legacy "RCTModulesConformingToProtocolsProvider.h" sho " `; -exports[`execute test-app-legacy "RCTModulesConformingToProtocolsProvider.mm" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactCodegen/RCTModulesConformingToProtocolsProvider.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -737,7 +775,7 @@ exports[`execute test-app-legacy "RCTModulesConformingToProtocolsProvider.mm" sh " `; -exports[`execute test-app-legacy "RCTThirdPartyComponentsProvider.h" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactCodegen/RCTThirdPartyComponentsProvider.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -757,7 +795,7 @@ exports[`execute test-app-legacy "RCTThirdPartyComponentsProvider.h" should matc " `; -exports[`execute test-app-legacy "RCTThirdPartyComponentsProvider.mm" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactCodegen/RCTThirdPartyComponentsProvider.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -791,7 +829,7 @@ exports[`execute test-app-legacy "RCTThirdPartyComponentsProvider.mm" should mat " `; -exports[`execute test-app-legacy "RCTUnstableModulesRequiringMainQueueSetupProvider.h" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactCodegen/RCTUnstableModulesRequiringMainQueueSetupProvider.h" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -809,7 +847,7 @@ exports[`execute test-app-legacy "RCTUnstableModulesRequiringMainQueueSetupProvi " `; -exports[`execute test-app-legacy "RCTUnstableModulesRequiringMainQueueSetupProvider.mm" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactCodegen/RCTUnstableModulesRequiringMainQueueSetupProvider.mm" should match snapshot 1`] = ` "/* * Copyright (c) Meta Platforms, Inc. and affiliates. * @@ -834,45 +872,7 @@ exports[`execute test-app-legacy "RCTUnstableModulesRequiringMainQueueSetupProvi " `; -exports[`execute test-app-legacy "ReactAppDependencyProvider.podspec" should match snapshot 1`] = ` -"# Copyright (c) Meta Platforms, Inc. and affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -version = \\"1000.0.0\\" -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 = \\"ReactAppDependencyProvider\\" - s.version = version - s.summary = \\"The third party dependency provider for the app\\" - s.homepage = \\"https://reactnative.dev/\\" - s.documentation_url = \\"https://reactnative.dev/\\" - s.license = \\"MIT\\" - s.author = \\"Meta Platforms, Inc. and its affiliates\\" - s.platforms = min_supported_versions - s.source = source - s.source_files = \\"**/RCTAppDependencyProvider.{h,mm}\\" - - # This guard prevent to install the dependencies when we run \`pod install\` in the old architecture. - s.pod_target_xcconfig = { - \\"CLANG_CXX_LANGUAGE_STANDARD\\" => rct_cxx_language_standard(), - \\"DEFINES_MODULE\\" => \\"YES\\" - } - - s.dependency \\"ReactCodegen\\" -end -" -`; - -exports[`execute test-app-legacy "ReactCodegen.podspec" should match snapshot 1`] = ` +exports[`execute test-app-legacy "ReactCodegen/ReactCodegen.podspec" should match snapshot 1`] = ` "# Copyright (c) Meta Platforms, Inc. and affiliates. # # This source code is licensed under the MIT license found in the diff --git a/packages/react-native/scripts/codegen/__tests__/generate-artifacts-executor-test.js b/packages/react-native/scripts/codegen/__tests__/generate-artifacts-executor-test.js index 73813156c576..729a1147fa7d 100644 --- a/packages/react-native/scripts/codegen/__tests__/generate-artifacts-executor-test.js +++ b/packages/react-native/scripts/codegen/__tests__/generate-artifacts-executor-test.js @@ -42,18 +42,18 @@ const packageJson = JSON.stringify({ }); [ - 'RCTAppDependencyProvider.h', - 'RCTAppDependencyProvider.mm', - 'RCTModuleProviders.h', - 'RCTModuleProviders.mm', - 'RCTModulesConformingToProtocolsProvider.h', - 'RCTModulesConformingToProtocolsProvider.mm', - 'RCTThirdPartyComponentsProvider.h', - 'RCTThirdPartyComponentsProvider.mm', - 'ReactAppDependencyProvider.podspec', - 'ReactCodegen.podspec', - 'RCTUnstableModulesRequiringMainQueueSetupProvider.h', - 'RCTUnstableModulesRequiringMainQueueSetupProvider.mm', + 'ReactAppDependencyProvider/RCTAppDependencyProvider.h', + 'ReactAppDependencyProvider/RCTAppDependencyProvider.mm', + 'ReactCodegen/RCTModuleProviders.h', + 'ReactCodegen/RCTModuleProviders.mm', + 'ReactCodegen/RCTModulesConformingToProtocolsProvider.h', + 'ReactCodegen/RCTModulesConformingToProtocolsProvider.mm', + 'ReactCodegen/RCTThirdPartyComponentsProvider.h', + 'ReactCodegen/RCTThirdPartyComponentsProvider.mm', + 'ReactAppDependencyProvider/ReactAppDependencyProvider.podspec', + 'ReactCodegen/ReactCodegen.podspec', + 'ReactCodegen/RCTUnstableModulesRequiringMainQueueSetupProvider.h', + 'ReactCodegen/RCTUnstableModulesRequiringMainQueueSetupProvider.mm', ].forEach(file => { it(`"${file}" should match snapshot`, () => { const generatedFileDir = path.join(outputDir, 'build/generated/ios'); diff --git a/packages/react-native/scripts/codegen/generate-artifacts-executor/generatePackageSwift.js b/packages/react-native/scripts/codegen/generate-artifacts-executor/generatePackageSwift.js new file mode 100644 index 000000000000..3e669401aa88 --- /dev/null +++ b/packages/react-native/scripts/codegen/generate-artifacts-executor/generatePackageSwift.js @@ -0,0 +1,44 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +'use strict'; +const {TEMPLATES_FOLDER_PATH} = require('./constants'); +const {codegenLog} = require('./utils'); +const fs = require('fs'); +const path = require('path'); + +const PACKAGE_SWIFT_TEMPLATE_PATH = path.join( + TEMPLATES_FOLDER_PATH, + 'Package.swift.template', +); + +function generatePackageSwift( + projectRoot /*: string */, + outputDir /*: string */, + reactNativePath /*: string */, +) { + const fullOutputPath = path.join(projectRoot, outputDir); + fs.mkdirSync(outputDir, {recursive: true}); + // Generate PAckage.swift File + codegenLog('Generating Package.swift'); + const templateH = fs + .readFileSync(PACKAGE_SWIFT_TEMPLATE_PATH, 'utf8') + .replace( + /{reactNativePath}/, + path.relative(fullOutputPath, reactNativePath), + ); + const finalPathH = path.join(outputDir, 'Package.swift'); + fs.writeFileSync(finalPathH, templateH); + codegenLog(`Generated artifact: ${finalPathH}`); +} + +module.exports = { + generatePackageSwift, +}; diff --git a/packages/react-native/scripts/codegen/generate-artifacts-executor/index.js b/packages/react-native/scripts/codegen/generate-artifacts-executor/index.js index a3c52e39f386..35a7b0265a12 100644 --- a/packages/react-native/scripts/codegen/generate-artifacts-executor/index.js +++ b/packages/react-native/scripts/codegen/generate-artifacts-executor/index.js @@ -22,6 +22,7 @@ const { } = require('./generateAppDependencyProvider'); const {generateCustomURLHandlers} = require('./generateCustomURLHandlers'); const {generateNativeCode} = require('./generateNativeCode'); +const {generatePackageSwift} = require('./generatePackageSwift'); const {generateRCTModuleProviders} = require('./generateRCTModuleProviders'); const { generateRCTThirdPartyComponents, @@ -37,6 +38,7 @@ const { codegenLog, findCodegenEnabledLibraries, findDisabledLibrariesByPlatform, + findReactNativeRootPath, pkgJsonIncludesGeneratedCode, readPkgJsonInDirectory, readReactNativeConfig, @@ -125,10 +127,15 @@ function execute( platform, ); + const reactCodegenOutputPath = + platform === 'android' + ? outputPath + : path.join(outputPath, 'ReactCodegen'); + if (runReactNativeCodegen) { const schemaInfos = generateSchemaInfos(libraries); generateNativeCode( - outputPath, + reactCodegenOutputPath, schemaInfos.filter(schemaInfo => mustGenerateNativeCode(projectRoot, schemaInfo), ), @@ -139,20 +146,32 @@ function execute( if (source === 'app' && platform !== 'android') { // These components are only required by apps, not by libraries and are Apple specific. - generateRCTThirdPartyComponents(libraries, outputPath); - generateRCTModuleProviders(projectRoot, pkgJson, libraries, outputPath); - generateCustomURLHandlers(libraries, outputPath); + generateRCTThirdPartyComponents(libraries, reactCodegenOutputPath); + generateRCTModuleProviders( + projectRoot, + pkgJson, + libraries, + reactCodegenOutputPath, + ); + generateCustomURLHandlers(libraries, reactCodegenOutputPath); generateUnstableModulesRequiringMainQueueSetupProvider( libraries, - outputPath, + reactCodegenOutputPath, + ); + generateAppDependencyProvider( + path.join(outputPath, 'ReactAppDependencyProvider'), ); - generateAppDependencyProvider(outputPath); generateReactCodegenPodspec( projectRoot, pkgJson, - outputPath, + reactCodegenOutputPath, baseOutputPath, ); + generatePackageSwift( + projectRoot, + outputPath, + findReactNativeRootPath(projectRoot), + ); } cleanupEmptyFilesAndFolders(outputPath); diff --git a/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js b/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js index 5cb42c28e70e..97a4b89c540e 100644 --- a/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js +++ b/packages/react-native/scripts/codegen/generate-artifacts-executor/utils.js @@ -461,6 +461,17 @@ function findDisabledLibrariesByPlatform( ); } +function findReactNativeRootPath(projectRoot /* : string */) /* : string */ { + const reactNativePackageJsonPath = require.resolve( + path.join('react-native', 'package.json'), + { + paths: [projectRoot], + }, + ); + + return path.dirname(reactNativePackageJsonPath); +} + module.exports = { buildCodegenIfNeeded, pkgJsonIncludesGeneratedCode, @@ -474,4 +485,5 @@ module.exports = { parseiOSAnnotations, readReactNativeConfig, findDisabledLibrariesByPlatform, + findReactNativeRootPath, }; diff --git a/packages/react-native/scripts/codegen/templates/Package.swift.template b/packages/react-native/scripts/codegen/templates/Package.swift.template new file mode 100644 index 000000000000..788b7e8c055e --- /dev/null +++ b/packages/react-native/scripts/codegen/templates/Package.swift.template @@ -0,0 +1,59 @@ +// swift-tools-version: 6.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "React-GeneratedCode", + platforms: [.iOS(.v15), .macCatalyst(SupportedPlatform.MacCatalystVersion.v13)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "ReactCodegen", + targets: ["ReactCodegen"]), + .library( + name: "ReactAppDependencyProvider", + targets: ["ReactAppDependencyProvider"]), + ], + dependencies: [ + .package(name: "React", path: "{reactNativePath}") + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "ReactCodegen", + dependencies: ["React"], + path: "ReactCodegen", + exclude: ["ReactCodegen.podspec"], + publicHeadersPath: ".", + cSettings: [ + .headerSearchPath("headers") + ], + cxxSettings: [ + .headerSearchPath("headers"), + .unsafeFlags(["-std=c++20"]), + ], + linkerSettings: [ + .linkedFramework("Foundation") + ] + ), + .target( + name: "ReactAppDependencyProvider", + dependencies: ["ReactCodegen"], + path: "ReactAppDependencyProvider", + exclude: ["ReactAppDependencyProvider.podspec"], + publicHeadersPath: ".", + cSettings: [ + .headerSearchPath("headers"), + ], + cxxSettings: [ + .headerSearchPath("headers"), + .unsafeFlags(["-std=c++20"]), + ], + linkerSettings: [ + .linkedFramework("Foundation") + ] + ) + ] +) diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 7dff2b950633..3b2136352df4 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -24,7 +24,8 @@ # Importing to expose use_native_modules! require_relative './cocoapods/autolinking.rb' -$CODEGEN_OUTPUT_DIR = 'build/generated/ios' +$CODEGEN_OUTPUT_DIR = 'build/generated/ios/ReactCodegen' +$APP_DEPENDENCY_PROVIDER_OUTPUT_DIR = 'build/generated/ios/ReactAppDependencyProvider' $CODEGEN_COMPONENT_DIR = 'react/renderer/components' $CODEGEN_MODULE_DIR = '.' @@ -199,7 +200,7 @@ def use_react_native! ( end pod 'ReactCodegen', :path => $CODEGEN_OUTPUT_DIR, :modular_headers => true - pod 'ReactAppDependencyProvider', :path => $CODEGEN_OUTPUT_DIR, :modular_headers => true + pod 'ReactAppDependencyProvider', :path => $APP_DEPENDENCY_PROVIDER_OUTPUT_DIR, :modular_headers => true # Not needed, but run_codegen expects this to be set. folly_config = get_folly_config() run_codegen!( diff --git a/packages/react-native/scripts/swiftpm/prepare-app-dependencies-headers.js b/packages/react-native/scripts/swiftpm/prepare-app-dependencies-headers.js new file mode 100644 index 000000000000..315b34e25929 --- /dev/null +++ b/packages/react-native/scripts/swiftpm/prepare-app-dependencies-headers.js @@ -0,0 +1,831 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +const {execSync} = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +/*:: +type RequiredHeaders = 'react-native' | 'codegen' | 'third-party-dependencies' | 'all'; +*/ + +/** + * Prepares app dependencies headers for SwiftPM integration + * @param {string} reactNativePath - Path to the React Native directory + * @param {string} iosAppPath - Path to the iOS app directory + * @param {string} outputFolder - Path to the output folder where headers will be hard linked + * @param {string} requiredHeaders - Type of headers to include: 'react-native', 'codegen', 'third-party-dependencies', or 'all' + */ +function prepareAppDependenciesHeaders( + reactNativePath /*: string */, + iosAppPath /*: string */, + outputFolder /*: string */, + requiredHeaders /*: RequiredHeaders */, +) /*: void */ { + // Validate parameters + if (!reactNativePath || !iosAppPath || !outputFolder || !requiredHeaders) { + throw new Error( + 'Missing required parameters. Usage: prepareAppDependenciesHeaders(reactNativePath, iosAppPath, outputFolder, requiredHeaders)', + ); + } + + if ( + !['react-native', 'codegen', 'third-party-dependencies', 'all'].includes( + requiredHeaders, + ) + ) { + throw new Error( + "requiredHeaders must be one of: 'react-native', 'codegen', 'third-party-dependencies', 'all'", + ); + } + + // Validate paths exist + if (!fs.existsSync(reactNativePath)) { + throw new Error(`React Native path does not exist: ${reactNativePath}`); + } + + if (!fs.existsSync(iosAppPath)) { + throw new Error(`iOS app path does not exist: ${iosAppPath}`); + } + + // Create output folder if it doesn't exist + if (!fs.existsSync(outputFolder)) { + fs.mkdirSync(outputFolder, {recursive: true}); + console.log(`Created output folder: ${outputFolder}`); + } + + console.log('Preparing app dependencies headers...'); + console.log(`React Native path: ${reactNativePath}`); + console.log(`iOS app path: ${iosAppPath}`); + console.log(`Output folder: ${outputFolder}`); + console.log(`Required headers: ${requiredHeaders}`); + + try { + switch (requiredHeaders) { + case 'react-native': + hardlinkReactNativeHeaders(reactNativePath, outputFolder); + break; + case 'codegen': + hardlinkCodegenHeaders(reactNativePath, iosAppPath, outputFolder); + break; + case 'third-party-dependencies': + hardlinkThirdPartyDependenciesHeaders(reactNativePath, outputFolder); + break; + case 'all': + hardlinkReactNativeHeaders(reactNativePath, outputFolder); + hardlinkCodegenHeaders(reactNativePath, iosAppPath, outputFolder); + hardlinkThirdPartyDependenciesHeaders(reactNativePath, outputFolder); + break; + default: + throw new Error( + `Unsupported requiredHeaders value: ${requiredHeaders}`, + ); + } + + console.log('Successfully prepared app dependencies headers'); + } catch (error) { + console.error('Error preparing app dependencies headers:', error.message); + throw error; + } +} + +/** + * Create hard links for React Native headers in the output folder + * @param {string} reactNativePath - Path to the React Native directory + * @param {string} outputFolder - Path to the output folder + * @param {string} folderName - Name of the folder where headers will be created (default: 'headers') + */ +function hardlinkReactNativeHeaders( + reactNativePath /*: string */, + outputFolder /*: string */, + folderName /*: string */ = 'headers', +) /*: void */ { + console.log('Creating hard links for React Native headers...'); + + const headersOutput = path.join(outputFolder, folderName); + if (!fs.existsSync(headersOutput)) { + fs.mkdirSync(headersOutput, {recursive: true}); + } + + let totalLinkedCount = 0; + + // Create React subdirectory for React and Libraries headers + const reactHeadersOutput = path.join(headersOutput, 'React'); + if (!fs.existsSync(reactHeadersOutput)) { + fs.mkdirSync(reactHeadersOutput, {recursive: true}); + } + + // Define custom mappings for Libraries folder + const reactMappings = { + 'FBReactNativeSpec/': path.join(headersOutput, 'FBReactNativeSpec'), + }; + + // 1. Process React folder - flatten structure, exclude 'includes', 'headers', and 'tests' folders + const reactPath = path.join(reactNativePath, 'React'); + if (fs.existsSync(reactPath)) { + console.log('Processing React folder...'); + const reactCount = hardlinkHeadersFromPath( + reactPath, + reactHeadersOutput, + false, + ['includes', 'headers', 'tests'], + reactMappings, + ); + totalLinkedCount += reactCount; + console.log(`Created ${reactCount} hard links from React folder`); + } + + // 2. Process Libraries folder with custom mapping for RCTRequired + const librariesPath = path.join(reactNativePath, 'Libraries'); + if (fs.existsSync(librariesPath)) { + console.log('Processing Libraries folder...'); + + // Define custom mappings for Libraries folder + const librariesMappings = { + 'Required/': path.join(headersOutput, 'RCTRequired'), + 'TypeSafety/': path.join(headersOutput, 'RCTTypeSafety'), + 'FBLazyVector/': path.join(headersOutput, 'FBLazyVector'), + 'FBReactNativeSpec/': path.join(headersOutput, 'FBReactNativeSpec'), + }; + + const librariesCount = hardlinkHeadersFromPath( + librariesPath, + reactHeadersOutput, + false, + ['tests'], + librariesMappings, + ); + totalLinkedCount += librariesCount; + console.log(`Created ${librariesCount} hard links from Libraries folder`); + } + + // 3. Process ReactApple folder - special structure preservation + const reactApplePath = path.join(reactNativePath, 'ReactApple'); + if (fs.existsSync(reactApplePath)) { + console.log('Processing ReactApple folder...'); + const reactAppleCount = hardlinkReactAppleHeaders( + reactApplePath, + headersOutput, + ); + totalLinkedCount += reactAppleCount; + console.log(`Created ${reactAppleCount} hard links from ReactApple folder`); + } + + // 4. Process ReactCommon folder - conditional structure preservation + const reactCommonPath = path.join(reactNativePath, 'ReactCommon'); + if (fs.existsSync(reactCommonPath)) { + console.log('Processing ReactCommon folder...'); + // Define paths that should be flattened in ReactCommon folder + const flattenPaths = ['react/nativemodule/core/platform/ios']; + // Define special mappings for flattening specific directories + const specialMapping = { + 'yoga/': 'yoga', + 'cxxreact/': 'cxxreact', + 'jsinspector-modern/': 'jsinspector-modern', + 'jserrorhandler/': 'jserrorhandler', + 'oscompat/': 'oscompat', + }; + const reactCommonCount = hardlinkReactCommonHeaders( + reactCommonPath, + headersOutput, + flattenPaths, + specialMapping, + ); + totalLinkedCount += reactCommonCount; + console.log( + `Created ${reactCommonCount} hard links from ReactCommon folder`, + ); + } + + console.log( + `Created hard links for ${totalLinkedCount} React Native headers total`, + ); +} + +/** + * Helper function to create hard links from a source path + * @param {string} sourcePath - Source directory to search for headers + * @param {string} outputPath - Default output directory for hard links + * @param {boolean} preserveStructure - Whether to preserve directory structure + * @param {Array} excludeFolders - Folder names to exclude + * @param {Object} customMappings - Custom path mappings (prefix -> output path) + * @returns {number} Number of hard links created + */ +function hardlinkHeadersFromPath( + sourcePath /*: string */, + outputPath /*: string */, + preserveStructure /*: boolean */, + excludeFolders /*: Array */, + customMappings /*: {[string]: string} */ = {}, +) /*: number */ { + let linkedCount = 0; + + try { + // Build find command with exclusions + let findCommand = `find "${sourcePath}" -name "*.h" -type f`; + + // Add exclusions for specified folders + excludeFolders.forEach(folder => { + findCommand += ` | grep -v "/${folder}/"`; + }); + + const result = execSync(findCommand, { + encoding: 'utf8', + stdio: 'pipe', + }); + + const headerFiles = result + .trim() + .split('\n') + .filter(p => p.length > 0); + + headerFiles.forEach(sourceHeaderPath => { + if (fs.existsSync(sourceHeaderPath)) { + const relativePath = path.relative(sourcePath, sourceHeaderPath); + let destPath = ''; + let mappedOutputPath = outputPath; + + // Check for custom mappings first + for (const [prefix, customOutput] of Object.entries(customMappings)) { + if (relativePath.startsWith(prefix)) { + mappedOutputPath = customOutput; + console.log(` Custom mapping: ${prefix} -> ${customOutput}`); + break; + } + } + + if (preserveStructure) { + // Preserve directory structure + destPath = path.join(mappedOutputPath, relativePath); + } else { + // Flatten structure - just use the header filename + const headerName = path.basename(sourceHeaderPath); + destPath = path.join(mappedOutputPath, headerName); + } + + // Create destination directory if it doesn't exist + const destDir = path.dirname(destPath); + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, {recursive: true}); + } + + // Remove existing hard link if it exists + if (fs.existsSync(destPath)) { + fs.unlinkSync(destPath); + } + + // Create hard link + fs.linkSync(sourceHeaderPath, destPath); + linkedCount++; + } + }); + } catch (error) { + console.warn( + `Failed to process headers from ${sourcePath}:`, + error.message, + ); + } + + return linkedCount; +} + +/** + * Create hard links for ReactApple headers with special path logic + * Keeps the last path component before "Exported" as a subdirectory + * @param {string} reactApplePath - Path to ReactApple directory + * @param {string} headersOutput - Base headers output directory + * @returns {number} Number of hard links created + */ +function hardlinkReactAppleHeaders( + reactApplePath /*: string */, + headersOutput /*: string */, +) /*: number */ { + let linkedCount = 0; + + console.log(`Searching for headers in: ${reactApplePath}`); + + try { + const result = execSync( + `find "${reactApplePath}" -name "*.h" -type f | grep -v "/tests/"`, + { + encoding: 'utf8', + stdio: 'pipe', + }, + ); + + const headerFiles = result + .trim() + .split('\n') + .filter(p => p.length > 0); + console.log(`Found ${headerFiles.length} header files in ReactApple`); + + headerFiles.forEach(sourceHeaderPath => { + if (fs.existsSync(sourceHeaderPath)) { + // Calculate relative path from ReactApple base + const relativePath = path.relative(reactApplePath, sourceHeaderPath); + console.log(`Processing: ${relativePath}`); + + // Split path into components + const pathComponents = relativePath.split(path.sep); + + // Find "Exported" component + const exportedIndex = pathComponents.findIndex( + component => component === 'Exported', + ); + + let destPath = ''; + const headerName = path.basename(sourceHeaderPath); + + if (exportedIndex !== -1 && exportedIndex > 0) { + // Get the component immediately before "Exported" + const lastComponentBeforeExported = pathComponents[exportedIndex - 1]; + destPath = path.join( + headersOutput, + lastComponentBeforeExported, + headerName, + ); + console.log( + ` -> ${lastComponentBeforeExported}/${headerName} (preserved component before Exported)`, + ); + } else { + // If no "Exported" found, flatten to headers folder + destPath = path.join(headersOutput, headerName); + console.log(` -> ${headerName} (no Exported found, flattened)`); + } + + const destDir = path.dirname(destPath); + + // Create destination directory if it doesn't exist + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, {recursive: true}); + } + + // Remove existing hard link if it exists + if (fs.existsSync(destPath)) { + fs.unlinkSync(destPath); + } + + try { + // Create hard link + fs.linkSync(sourceHeaderPath, destPath); + linkedCount++; + } catch (linkError) { + console.warn( + `Failed to create hard link from ${sourceHeaderPath} to ${destPath}: ${linkError.message}`, + ); + } + } + }); + } catch (error) { + console.warn(`Failed to process ReactApple headers:`, error.message); + } + + console.log(`Created ${linkedCount} hard links from ReactApple`); + return linkedCount; +} + +/** + * Create hard links for ReactCommon headers with conditional path logic + * @param {string} reactCommonPath - Path to ReactCommon directory + * @param {string} headersOutput - Base headers output directory + * @param {Array} flattenPaths - Array of relative paths that should be flattened in ReactCommon folder + * @param {Object} specialMapping - Map of prefix -> destination folder for special cases + * @returns {number} Number of hard links created + */ +function hardlinkReactCommonHeaders( + reactCommonPath /*: string */, + headersOutput /*: string */, + flattenPaths /*: Array */ = [], + specialMapping /*: {[string]: string} */ = {}, +) /*: number */ { + let linkedCount = 0; + + console.log(`Searching for headers in: ${reactCommonPath}`); + console.log(`Flatten paths: ${flattenPaths.join(', ')}`); + + try { + const result = execSync( + `find "${reactCommonPath}" -name "*.h" -type f | grep -v "/tests/"`, + { + encoding: 'utf8', + stdio: 'pipe', + }, + ); + + const headerFiles = result + .trim() + .split('\n') + .filter(p => p.length > 0); + console.log(`Found ${headerFiles.length} header files in ReactCommon`); + + // Create ReactCommon subdirectory for headers that should go there + const reactCommonHeadersOutput = path.join(headersOutput, 'ReactCommon'); + if (!fs.existsSync(reactCommonHeadersOutput)) { + fs.mkdirSync(reactCommonHeadersOutput, {recursive: true}); + } + + headerFiles.forEach(sourceHeaderPath => { + if (fs.existsSync(sourceHeaderPath)) { + // Calculate relative path from ReactCommon base + const relativePath = path.relative(reactCommonPath, sourceHeaderPath); + + let destPath = ''; + + // Check for ReactCommon/**/ReactCommon/header.h pattern + // Since relativePath is calculated from ReactCommon base, any "ReactCommon" component + // in the relative path indicates a nested ReactCommon folder + const pathComponents = relativePath.split(path.sep); + const hasNestedReactCommon = pathComponents.includes('ReactCommon'); + + if (hasNestedReactCommon) { + // Handle ReactCommon/**/ReactCommon/header.h pattern - flatten to ReactCommon folder + const headerName = path.basename(sourceHeaderPath); + destPath = path.join(reactCommonHeadersOutput, headerName); + console.log( + ` -> ReactCommon/${headerName} (flattened from nested ReactCommon pattern: ${relativePath})`, + ); + } else { + // Check if path should be flattened in ReactCommon folder + const shouldFlatten = flattenPaths.some(flattenPath => { + const matchPattern = flattenPath + '/'; + const matches = relativePath.startsWith(matchPattern); + if (matches) { + console.log( + ` Flattening: ${relativePath} (matches ${flattenPath})`, + ); + } + return matches; + }); + + if (shouldFlatten) { + // Flatten to ReactCommon folder + const headerName = path.basename(sourceHeaderPath); + destPath = path.join(reactCommonHeadersOutput, headerName); + console.log(` -> ${headerName} (flattened to ReactCommon)`); + } else if (relativePath.startsWith('react/')) { + // Handle Switch special case + if (relativePath.startsWith('react/renderer/components/switch')) { + if (relativePath.includes('androidswitch')) { + return; + } + + destPath = path.join( + headersOutput, + 'react', + 'renderer', + 'components', + 'switch', + ); + + // the header files are in the react/renderer/components/switch/iosswitch/react/renderer/components/switch + // we need the header file. + let headerFileName = path.basename(relativePath); + destPath = path.join(destPath, headerFileName); + + console.log( + ` -> react/renderer/components/switch/${headerFileName} handling special switch case)`, + ); + } else { + // Handle platform-specific headers with pattern: + // react/renderer/components/view/platform/{cxx,android}/react/renderer/components/view/header.h + const platformMatch = relativePath.match( + /^(react\/.*?)\/platform\/([^/]+)\/react\/(.*)$/, + ); + + if (platformMatch) { + const [, , platform, remainingPath] = platformMatch; + const supportedPlatforms = ['ios', 'cxx']; + const ignoredPlatforms = ['android', 'windows', 'macos']; + + if (supportedPlatforms.includes(platform)) { + // Flatten to headers/react/renderer/components/view/ + destPath = path.join(headersOutput, 'react', remainingPath); + console.log( + ` -> react/${remainingPath} (flattened from platform-specific ${platform})`, + ); + } else if (ignoredPlatforms.includes(platform)) { + // Skip headers for ignored platforms + console.log( + ` -> Skipping header for ignored platform: ${platform}`, + ); + return; // Skip this header file + } else { + // Unknown platform, preserve the original structure + destPath = path.join(headersOutput, relativePath); + console.log( + ` -> ${relativePath} (unknown platform: ${platform}, preserved structure)`, + ); + } + } else { + // If path starts with 'react/' but doesn't match platform pattern, preserve the relative path under headers/ + destPath = path.join(headersOutput, relativePath); + console.log(` -> ${relativePath} (preserved under headers/)`); + } + } + } else { + // Check for special mappings + let specialCaseMatched = false; + for (const [prefix, destinationFolder] of Object.entries( + specialMapping, + )) { + if (relativePath.startsWith(prefix)) { + let mappedPath = relativePath; + + // Special handling for yoga - remove duplicated yoga/ prefix + if ( + prefix === 'yoga/' && + relativePath.startsWith('yoga/yoga/') + ) { + mappedPath = relativePath.substring(5); // Remove 'yoga/' (5 characters) + } + + destPath = path.join( + headersOutput, + destinationFolder, + mappedPath.substring(prefix.length), + ); + console.log( + ` -> ${destinationFolder}/${mappedPath.substring(prefix.length)} (${prefix.slice(0, -1)} headers flattened, bypassing ReactCommon)`, + ); + specialCaseMatched = true; + break; + } + } + + if (!specialCaseMatched) { + // Otherwise, put it under headers/ReactCommon/ with structure preserved + destPath = path.join(reactCommonHeadersOutput, relativePath); + console.log( + ` -> ReactCommon/${relativePath} (structured under ReactCommon)`, + ); + } + } + } + + const destDir = path.dirname(destPath); + + // Create destination directory if it doesn't exist + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, {recursive: true}); + } + + // Remove existing hard link if it exists + if (fs.existsSync(destPath)) { + fs.unlinkSync(destPath); + } + + try { + // Create hard link + fs.linkSync(sourceHeaderPath, destPath); + linkedCount++; + } catch (linkError) { + console.warn( + `Failed to create hard link from ${sourceHeaderPath} to ${destPath}: ${linkError.message}`, + ); + } + } + }); + } catch (error) { + console.warn(`Failed to process ReactCommon headers:`, error.message); + } + + console.log(`Created ${linkedCount} hard links from ReactCommon`); + return linkedCount; +} + +/** + * Create hard links for third-party dependencies headers in the output folder + * @param {string} reactNativePath - Path to the React Native directory + * @param {string} outputFolder - Path to the output folder + * @param {string} folderName - Name of the folder where headers will be created (default: 'headers') + */ +function hardlinkThirdPartyDependenciesHeaders( + reactNativePath /*: string */, + outputFolder /*: string */, + folderName /*: string */ = 'headers', +) /*: void */ { + console.log('Creating hard links for Third-Party Dependencies headers...'); + + // Look for ReactNativeDependencies.xcframework/Headers folder specifically + const thirdPartyHeadersPath = path.join( + reactNativePath, + 'third-party', + 'ReactNativeDependencies.xcframework', + 'Headers', + ); + + if (!fs.existsSync(thirdPartyHeadersPath)) { + console.warn( + `Third-party dependencies headers path does not exist: ${thirdPartyHeadersPath}`, + ); + return; + } + + const headersOutput = path.join(outputFolder, folderName); + if (!fs.existsSync(headersOutput)) { + fs.mkdirSync(headersOutput, {recursive: true}); + } + + // Find all .h and .hpp files recursively in third-party headers, exclude 'tests' folders + try { + const result = execSync( + `find "${thirdPartyHeadersPath}" \\( -name "*.h" -o -name "*.hpp" \\) -type f | grep -v "/tests/"`, + { + encoding: 'utf8', + stdio: 'pipe', + }, + ); + + const headerFiles = result + .trim() + .split('\n') + .filter(p => p.length > 0); + let linkedCount = 0; + + headerFiles.forEach(sourcePath => { + if (fs.existsSync(sourcePath)) { + // Calculate relative path from Headers base to preserve structure + const relativePath = path.relative(thirdPartyHeadersPath, sourcePath); + const destPath = path.join(headersOutput, relativePath); + const destDir = path.dirname(destPath); + + // Create destination directory if it doesn't exist + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, {recursive: true}); + } + + // Remove existing hard link if it exists + if (fs.existsSync(destPath)) { + fs.unlinkSync(destPath); + } + + // Create hard link + fs.linkSync(sourcePath, destPath); + linkedCount++; + } + }); + + console.log( + `Created hard links for ${linkedCount} Third-Party Dependencies headers with preserved directory structure`, + ); + } catch (error) { + console.warn( + 'Failed to create hard links for third-party dependencies headers:', + error.message, + ); + } +} + +/** + * Create hard links for Codegen headers in the output folder + */ +function hardlinkCodegenHeaders( + reactNativePath /*: string */, + iosAppPath /*: string */, + outputFolder /*: string */, +) /*: void */ { + console.log('Creating hard links for Codegen headers...'); + + // Look for ReactCodegen folder specifically + const reactCodegenPath = path.join( + iosAppPath, + 'build', + 'generated', + 'ios', + 'ReactCodegen', + ); + + if (!fs.existsSync(reactCodegenPath)) { + console.warn(`ReactCodegen path does not exist: ${reactCodegenPath}`); + return; + } + + const headersOutput = path.join(outputFolder, 'headers'); + if (!fs.existsSync(headersOutput)) { + fs.mkdirSync(headersOutput, {recursive: true}); + } + + // Create ReactCodegen subdirectory for headers without subpaths + const reactCodegenHeadersOutput = path.join(headersOutput, 'ReactCodegen'); + if (!fs.existsSync(reactCodegenHeadersOutput)) { + fs.mkdirSync(reactCodegenHeadersOutput, {recursive: true}); + } + + // Find all .h files recursively in ReactCodegen, excluding any 'headers' and 'tests' folders + try { + const result = execSync( + `find "${reactCodegenPath}" -name "*.h" -type f | grep -v "/headers/" | grep -v "/tests/"`, + { + encoding: 'utf8', + stdio: 'pipe', + }, + ); + + const headerFiles = result + .trim() + .split('\n') + .filter(p => p.length > 0); + let linkedCount = 0; + + headerFiles.forEach(sourcePath => { + if (fs.existsSync(sourcePath)) { + // Calculate relative path from ReactCodegen base + const relativePath = path.relative(reactCodegenPath, sourcePath); + + let destPath /*: string */ = ''; + + // If relative path contains no subpath (just a filename), put it in ReactCodegen folder + if (path.dirname(relativePath) === '.') { + destPath = path.join(reactCodegenHeadersOutput, relativePath); + } else { + // Otherwise, preserve the structure under headers/ + destPath = path.join(headersOutput, relativePath); + } + + const destDir = path.dirname(destPath); + + // Create destination directory if it doesn't exist + if (!fs.existsSync(destDir)) { + fs.mkdirSync(destDir, {recursive: true}); + } + + // Remove existing hard link if it exists + if (fs.existsSync(destPath)) { + fs.unlinkSync(destPath); + } + + // Create hard link + fs.linkSync(sourcePath, destPath); + linkedCount++; + } + }); + + console.log( + `Created hard links for ${linkedCount} Codegen headers with conditional directory structure`, + ); + } catch (error) { + console.warn( + 'Failed to create hard links for codegen headers:', + error.message, + ); + } +} + +// CLI usage +if (require.main === module) { + const args = process.argv.slice(2); + + if (args.length !== 4) { + console.error( + 'Usage: node prepare-app-dependencies-headers.js ', + ); + console.error( + ' required-headers: react-native | codegen | third-party-dependencies | all', + ); + process.exit(1); + } + + const extractRequiredHeadersValue = ( + value /*: string */, + ) /*: RequiredHeaders */ => { + const validValues /*: Array */ = [ + 'react-native', + 'codegen', + 'third-party-dependencies', + 'all', + ]; + if (!validValues.includes(value)) { + throw new Error(`Invalid required-headers value: ${value}`); + } + return value; + }; + + const [reactNativePath, iosAppPath, outputFolder, requiredHeaders] = args; + const requiredHeadersEnum /*: RequiredHeaders */ = + extractRequiredHeadersValue(requiredHeaders); + + try { + prepareAppDependenciesHeaders( + reactNativePath, + iosAppPath, + outputFolder, + requiredHeadersEnum, + ); + } catch (error) { + console.error('Error:', error.message); + process.exit(1); + } +} + +module.exports = { + prepareAppDependenciesHeaders, + hardlinkReactNativeHeaders, + hardlinkThirdPartyDependenciesHeaders, +}; diff --git a/packages/react-native/scripts/swiftpm/prepare-app.js b/packages/react-native/scripts/swiftpm/prepare-app.js new file mode 100644 index 000000000000..060dcfea694c --- /dev/null +++ b/packages/react-native/scripts/swiftpm/prepare-app.js @@ -0,0 +1,768 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +/** + * Script to automatically prepare an app for building from source with SwiftPM + * + * This script automates the steps described in: + * private/helloworld/ios/to-remove-instructions-to-build-from-source-with-spm + */ + +const codegenExecutor = require('../codegen/generate-artifacts-executor'); +// Import functions from other scripts +const { + hardlinkReactNativeHeaders, + hardlinkThirdPartyDependenciesHeaders, + prepareAppDependenciesHeaders, +} = require('./prepare-app-dependencies-headers'); +const {integrateSwiftPackagesInXcode} = require('./update-xcodeproject'); +const {execSync} = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +/*:: +type SwiftPackage = { + relativePath: string, + targets: Array, +}; +*/ + +/** + * Find the directory containing the Xcode project within the app path + * @param {string} appPath - The root app path to search in + * @param {string} xcodeProjectName - The name of the Xcode project file (e.g., 'HelloWorld.xcodeproj') + * @returns {string} - The path to the directory containing the Xcode project + */ +function findXcodeProjectDirectory( + appPath /*: string */, + xcodeProjectName /*: string */, +) /*: string */ { + try { + // Use find command to search for the Xcode project + const findCommand = `find "${appPath}" -name "${xcodeProjectName}" -type d -print`; + const result = execSync(findCommand, {encoding: 'utf8'}).trim(); + + if (!result) { + throw new Error( + `Xcode project '${xcodeProjectName}' not found in '${appPath}' or its subdirectories`, + ); + } + + // Return the directory containing the Xcode project (parent of the .xcodeproj file) + return path.dirname(result); + } catch (error) { + throw new Error( + `Failed to find Xcode project '${xcodeProjectName}': ${error.message}`, + ); + } +} + +/** + * Main function to prepare the app + * @param {string} appPath - Path to the app (e.g., '../../private/helloworld') + * @param {string} reactNativePath - Path to the React Native package (e.g., '.') + * @param {string} appXcodeProject - Name of the Xcode project (e.g., 'HelloWorld.xcodeproj') + * @param {string} targetName - Name of the app target (e.g., 'HelloWorld') + */ +async function prepareApp( + appPath /*: string */ = '../../private/helloworld', + reactNativePath /*: string */ = '.', + appXcodeProject /*: string */ = 'HelloWorld.xcodeproj', + targetName /*: string */ = 'HelloWorld', + additionalPackages /*: Array */ = [], +) /*: Promise */ { + console.log('🚀 Starting app preparation for SwiftPM build from source...'); + + // Resolve absolute paths + const absoluteAppPath = path.resolve(appPath); + const absoluteReactNativePath = path.resolve(reactNativePath); + + // Search for the Xcode project within the app path instead of assuming it's in 'ios' folder + const appIosPath = findXcodeProjectDirectory( + absoluteAppPath, + appXcodeProject, + ); + + console.log(`App path: ${absoluteAppPath}`); + console.log(`React Native path: ${absoluteReactNativePath}`); + console.log(`App iOS path: ${appIosPath}`); + + // Validate paths exist + if (!fs.existsSync(absoluteAppPath)) { + throw new Error(`App path does not exist: ${absoluteAppPath}`); + } + + if (!fs.existsSync(appIosPath)) { + throw new Error(`App iOS path does not exist: ${appIosPath}`); + } + + try { + // Step 1: Pod deintegrate from app directory + console.log('\n📦 Step 1: Running pod deintegrate...'); + await runPodDeintegrate(appIosPath); + + // Step 2: Run iOS prebuild with environment variables + console.log('\n🏗️ Step 2: Running iOS prebuild...'); + await runIosPrebuild(absoluteReactNativePath); + + // Step 3: Configure app for Swift integration + console.log('\n🏗️ Step 3: Configuring app for Swift integration...'); + await configureAppForSwift(absoluteReactNativePath); + + // Step 4: Modify BUILD_FROM_SOURCE setting + console.log('\n⚙️ Step 4: Setting BUILD_FROM_SOURCE to true...'); + await setBuildFromSource(absoluteReactNativePath); + + // Step 5: Create hard links + console.log('\n🔗 Step 5: Creating hard links...'); + await createHardlinks(absoluteReactNativePath); + + // Step 6: Generate codegen artifacts + console.log('\n🧬 Step 6: Generating codegen artifacts...'); + await generateCodegenArtifacts( + absoluteReactNativePath, + absoluteAppPath, + appIosPath, + ); + + // Step 7: Prepare app dependencies headers (3 times) + console.log('\n📂 Step 7: Preparing app dependencies headers...'); + await prepareHeaders(absoluteReactNativePath, appIosPath); + + // Step 8: Fix REACT_NATIVE_PATH in Xcode project + console.log('\n🔧 Step 8: Fixing REACT_NATIVE_PATH in Xcode project...'); + await fixReactNativePath( + appIosPath, + absoluteReactNativePath, + appXcodeProject, + ); + + // Step 9: Allow non-modular header imports in Xcode project + console.log( + '\n🔧 Step 9: Allowing non-modular header imports in Xcode project...', + ); + await allowNonModularHeaderImport(appIosPath, appXcodeProject); + + // Step 10: Integrate SwiftPM packages in Xcode + console.log('\n📦 Step 10: Integrating SwiftPM packages in Xcode...'); + await integrateSwiftPMPackages( + appIosPath, + absoluteReactNativePath, + absoluteAppPath, + appXcodeProject, + targetName, + additionalPackages, + ); + + // Step 11: Open Xcode project + console.log('\n📱 Step 11: Opening Xcode project...'); + await openXcodeProject(appIosPath, appXcodeProject); + + console.log('\n✅ App preparation completed successfully!'); + } catch (error) { + console.error('\n❌ Error during app preparation:', error.message); + throw error; + } +} + +/** + * Run pod deintegrate from app directory + */ +async function runPodDeintegrate( + appIosPath /*: string */, +) /*: Promise */ { + try { + console.log(`Running pod deintegrate in: ${appIosPath}`); + execSync('pod deintegrate', { + cwd: appIosPath, + stdio: 'inherit', + }); + console.log('✓ Pod deintegrate completed'); + } catch (error) { + console.warn( + '⚠️ Pod deintegrate failed (this might be expected if no Podfile.lock exists)', + ); + } +} + +/** + * Run iOS prebuild with environment variables + */ +async function runIosPrebuild( + reactNativePath /*: string */, +) /*: Promise */ { + console.log('Running iOS prebuild with nightly versions...'); + + const env = { + ...process.env, + RN_DEP_VERSION: 'nightly', + HERMES_VERSION: 'nightly', + }; + + try { + execSync('node scripts/ios-prebuild -s', { + cwd: reactNativePath, + env: env, + stdio: 'inherit', + }); + console.log('✓ iOS prebuild completed'); + } catch (error) { + throw new Error(`iOS prebuild failed: ${error.message}`); + } +} + +/** + * Configure app for Swift integration + */ +async function configureAppForSwift( + reactNativePath /*: string */, +) /*: Promise */ { + try { + console.log('Configuring app for Swift integration...'); + + // 1. Create hardlink from React-umbrella.h to React-umbrella.h + const sourceUmbrellaPath = path.join( + reactNativePath, + 'scripts', + 'ios-prebuild', + 'React-umbrella.h', + ); + const reactIncludesReactPath = path.join( + reactNativePath, + 'React', + 'includes', + 'React', + ); + const destUmbrellaPath = path.join( + reactIncludesReactPath, + 'React-umbrella.h', + ); + + // Ensure the React/includes/React directory exists + if (!fs.existsSync(reactIncludesReactPath)) { + fs.mkdirSync(reactIncludesReactPath, {recursive: true}); + } + + // Remove existing hardlink if it exists + if (fs.existsSync(destUmbrellaPath)) { + fs.unlinkSync(destUmbrellaPath); + } + + // Create hardlink for umbrella header + if (fs.existsSync(sourceUmbrellaPath)) { + fs.linkSync(sourceUmbrellaPath, destUmbrellaPath); + console.log( + `✓ Created hardlink: React-umbrella.h -> ${path.relative( + reactNativePath, + sourceUmbrellaPath, + )}`, + ); + } else { + throw new Error( + `Source umbrella header not found: ${sourceUmbrellaPath}`, + ); + } + + // 2. Generate module.modulemap file + const reactIncludesPath = path.join(reactNativePath, 'React', 'includes'); + const moduleMapPath = path.join(reactIncludesPath, 'module.modulemap'); + const absoluteUmbrellaPath = path.join( + reactNativePath, + 'React', + 'includes', + 'React', + 'React-umbrella.h', + ); + const moduleMapContent = `framework module React { + umbrella header "${absoluteUmbrellaPath}" + export * + module * { export * } +} +`; + + fs.writeFileSync(moduleMapPath, moduleMapContent, 'utf8'); + console.log('✓ Generated module.modulemap file'); + + console.log('✓ App configured for Swift integration'); + } catch (error) { + throw new Error(`Swift configuration failed: ${error.message}`); + } +} + +/** + * Set BUILD_FROM_SOURCE to true in Package.swift + */ +async function setBuildFromSource( + reactNativePath /*: string */, +) /*: Promise */ { + const packageSwiftPath = path.join(reactNativePath, 'Package.swift'); + + if (!fs.existsSync(packageSwiftPath)) { + throw new Error(`Package.swift not found at: ${packageSwiftPath}`); + } + + try { + console.log(`Updating BUILD_FROM_SOURCE in: ${packageSwiftPath}`); + + const content = fs.readFileSync(packageSwiftPath, 'utf8'); + + // Check if BUILD_FROM_SOURCE = false exists and replace it with true + if (content.includes('let BUILD_FROM_SOURCE = false')) { + const updatedContent = content.replace( + /let BUILD_FROM_SOURCE = false/g, + 'let BUILD_FROM_SOURCE = true', + ); + fs.writeFileSync(packageSwiftPath, updatedContent, 'utf8'); + console.log('✓ BUILD_FROM_SOURCE set to true in Package.swift'); + } else if (content.includes('let BUILD_FROM_SOURCE = true')) { + console.log( + '✓ BUILD_FROM_SOURCE is already set to true in Package.swift', + ); + } else { + console.warn( + '⚠️ BUILD_FROM_SOURCE declaration not found in Package.swift', + ); + } + } catch (error) { + throw new Error( + `Failed to update BUILD_FROM_SOURCE in Package.swift: ${error.message}`, + ); + } +} + +/** + * Create hard links for React Native headers in React/includes + */ +async function createHardlinks( + reactNativePath /*: string */, +) /*: Promise */ { + try { + console.log('Creating hard links for React Native headers...'); + const reactIncludesPath = path.join(reactNativePath, 'React'); + hardlinkReactNativeHeaders(reactNativePath, reactIncludesPath, 'includes'); + console.log('✓ React Native hard links created in React/includes'); + + console.log('Creating hard links for third-party dependencies...'); + hardlinkThirdPartyDependenciesHeaders( + reactNativePath, + reactIncludesPath, + 'includes', + ); + console.log( + '✓ Third-party dependencies hard links created in React/includes', + ); + } catch (error) { + throw new Error(`Hard link creation failed: ${error.message}`); + } +} + +/** + * Generate codegen artifacts using the executor + */ +async function generateCodegenArtifacts( + reactNativePath /*: string */, + appPath /*: string */, + appIosPath /*: string */, +) /*: Promise */ { + try { + console.log('Generating codegen artifacts...'); + + // Use the codegen executor directly + codegenExecutor.execute(appPath, 'ios', appIosPath, 'app'); + + console.log('✓ Codegen artifacts generated'); + } catch (error) { + throw new Error(`Codegen generation failed: ${error.message}`); + } +} + +/** + * Prepare app dependencies headers (3 separate calls) + */ +async function prepareHeaders( + reactNativePath /*: string */, + appIosPath /*: string */, +) /*: Promise */ { + const outputFolder = path.join( + appIosPath, + 'build', + 'generated', + 'ios', + 'ReactAppDependencyProvider', + ); + const codegenOutputFolder = path.join( + appIosPath, + 'build', + 'generated', + 'ios', + 'ReactCodegen', + ); + + try { + // 1. Prepare codegen headers + console.log('Preparing codegen headers...'); + prepareAppDependenciesHeaders( + reactNativePath, + appIosPath, + outputFolder, + 'codegen', + ); + console.log('✓ Codegen headers prepared'); + + // 2. Prepare react-native headers + console.log('Preparing react-native headers...'); + prepareAppDependenciesHeaders( + reactNativePath, + appIosPath, + codegenOutputFolder, + 'react-native', + ); + console.log('✓ React Native headers prepared'); + + // 3. Prepare third-party dependencies headers + console.log('Preparing third-party dependencies headers...'); + prepareAppDependenciesHeaders( + reactNativePath, + appIosPath, + codegenOutputFolder, + 'third-party-dependencies', + ); + console.log('✓ Third-party dependencies headers prepared'); + } catch (error) { + throw new Error(`Header preparation failed: ${error.message}`); + } +} + +/** + * Fix REACT_NATIVE_PATH in Xcode project + */ +async function fixReactNativePath( + appIosPath /*: string */, + reactNativePath /*: string */, + appXcodeProject /*: string */, +) /*: Promise */ { + const projectPath = path.join(appIosPath, appXcodeProject, 'project.pbxproj'); + + if (!fs.existsSync(projectPath)) { + throw new Error(`Xcode project file not found: ${projectPath}`); + } + + try { + console.log('Fixing REACT_NATIVE_PATH in Xcode project...'); + + // Calculate the relative path from iOS app to React Native package + const relativePath = path.relative(appIosPath, reactNativePath); + const newReactNativePathValue = `REACT_NATIVE_PATH = "\${PROJECT_DIR}/${relativePath}";`; + + // Read the project file + let content = fs.readFileSync(projectPath, 'utf8'); + const lines = content.split('\n'); + let foundReactNativePath = false; + let modifiedLines = []; + + // Process each line + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Check if line contains REACT_NATIVE_PATH = " + if (line.includes('REACT_NATIVE_PATH = "')) { + foundReactNativePath = true; + + // Check if line also contains {PODS_ROOT} + if (line.includes('{PODS_ROOT}')) { + // Replace the whole line with the new path + const match = line.match(/^\s*/); + if (!match) { + continue; + } + const indentation = match[0]; // Preserve indentation + modifiedLines.push(`${indentation}${newReactNativePathValue}`); + } else { + // Keep the line as is if it doesn't contain {PODS_ROOT} + modifiedLines.push(line); + } + } else { + modifiedLines.push(line); + } + } + + // If no REACT_NATIVE_PATH was found, add it after buildSettings = { + if (!foundReactNativePath) { + const finalLines = []; + + for (let i = 0; i < modifiedLines.length; i++) { + const line = modifiedLines[i]; + finalLines.push(line); + + // Check if line contains buildSettings = { + if (line.includes('buildSettings = {')) { + // Add REACT_NATIVE_PATH on the next line with appropriate indentation + const match = line.match(/^\s*/); + if (!match) { + continue; + } + const indentation = match[0] + '\t'; // Add one more level of indentation + finalLines.push(`${indentation}${newReactNativePathValue}`); + } + } + + modifiedLines = finalLines; + } + + // Join lines back together and write to file + const updatedContent = modifiedLines.join('\n'); + fs.writeFileSync(projectPath, updatedContent, 'utf8'); + + console.log( + `✓ REACT_NATIVE_PATH fixed to: \${PROJECT_DIR}/${relativePath}`, + ); + } catch (error) { + throw new Error(`Failed to fix REACT_NATIVE_PATH: ${error.message}`); + } +} + +/** + * Allow non-modular header imports in Xcode project + */ +async function allowNonModularHeaderImport( + appIosPath /*: string */, + appXcodeProject /*: string */, +) /*: Promise */ { + const projectPath = path.join(appIosPath, appXcodeProject, 'project.pbxproj'); + + if (!fs.existsSync(projectPath)) { + throw new Error(`Xcode project file not found: ${projectPath}`); + } + + try { + console.log( + 'Setting CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES in Xcode project...', + ); + + const newClangSettingValue = `CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = YES;`; + + // Read the project file + let content = fs.readFileSync(projectPath, 'utf8'); + const lines = content.split('\n'); + let foundClangSetting = false; + let modifiedLines = []; + + // Process each line + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Check if line contains CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = + if ( + line.includes( + 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES = ', + ) + ) { + foundClangSetting = true; + + // Replace the whole line with the new setting + const match = line.match(/^\s*/); + if (!match) { + continue; + } + const indentation = match[0]; // Preserve indentation + modifiedLines.push(`${indentation}${newClangSettingValue}`); + } else { + modifiedLines.push(line); + } + } + + // If no CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES was found, add it after buildSettings = { + if (!foundClangSetting) { + const finalLines = []; + + for (let i = 0; i < modifiedLines.length; i++) { + const line = modifiedLines[i]; + finalLines.push(line); + + // Check if line contains buildSettings = { + if (line.includes('buildSettings = {')) { + // Add CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES on the next line with appropriate indentation + const match = line.match(/^\s*/); + if (!match) { + continue; + } + const indentation = match[0] + '\t'; // Add one more level of indentation + finalLines.push(`${indentation}${newClangSettingValue}`); + } + } + + modifiedLines = finalLines; + } + + // Join lines back together and write to file + const updatedContent = modifiedLines.join('\n'); + fs.writeFileSync(projectPath, updatedContent, 'utf8'); + + console.log( + '✓ CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES set to YES', + ); + } catch (error) { + throw new Error( + `Failed to set CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES: ${error.message}`, + ); + } +} + +/** + * Integrate SwiftPM packages into Xcode project + */ +async function integrateSwiftPMPackages( + appIosPath /*: string */, + reactNativePath /*: string */, + appPath /*: string */, + appXcodeProject /*: string */, + targetName /*: string */, + additionalPackages /*: Array */, +) /*: Promise */ { + try { + console.log('Preparing SwiftPM package integrations...'); + + // Calculate relative paths from appIosPath + const relativeReactNativePath = path.relative(appIosPath, reactNativePath); + const relativeGeneratedPath = path.join('build', 'generated', 'ios'); + + // Create PackageSwift objects + const packageSwiftObjects = [ + ...additionalPackages, + { + relativePath: relativeReactNativePath, + targets: ['React'], + }, + { + relativePath: relativeGeneratedPath, + targets: ['ReactCodegen', 'ReactAppDependencyProvider'], + }, + ]; + + const xcodeProjectPath = path.join(appIosPath, appXcodeProject); + + // Call integrateSwiftPackagesInXcode function + integrateSwiftPackagesInXcode( + xcodeProjectPath, + packageSwiftObjects, + targetName, + ); + + console.log('✓ SwiftPM packages integrated into Xcode project'); + } catch (error) { + throw new Error(`SwiftPM integration failed: ${error.message}`); + } +} + +/** + * Open Xcode project + */ +async function openXcodeProject( + appIosPath /*: string */, + appXcodeProject /*: string */, +) /*: Promise */ { + const xcodeProjectPath = path.join(appIosPath, appXcodeProject); + + if (!fs.existsSync(xcodeProjectPath)) { + throw new Error(`Xcode project not found: ${xcodeProjectPath}`); + } + + try { + console.log(`Opening Xcode project: ${xcodeProjectPath}`); + execSync(`open "${xcodeProjectPath}"`, { + stdio: 'inherit', + }); + console.log('✓ Xcode project opened'); + } catch (error) { + throw new Error(`Failed to open Xcode project: ${error.message}`); + } +} + +// CLI usage +if (require.main === module) { + const args = process.argv.slice(2); + + let appPath = '../../private/helloworld'; + let reactNativePath = '.'; + let appXcodeProject = 'HelloWorld.xcodeproj'; + let targetName = 'HelloWorld'; + let additionalPackages /*: Array */ = []; + + // try to load args from the swiftpm.config.js file that is + // in the app directory. Assumption: this script is invoked from the app directory + try { + const configJSFile = path.join(process.cwd(), 'swiftpm.config.js'); + // $FlowFixMe[unsupported-syntax] + const swiftPMConfig = require(configJSFile); + + appPath = swiftPMConfig.appPath; + reactNativePath = swiftPMConfig.reactNativePath; + appXcodeProject = swiftPMConfig.appXcodeProject; + targetName = swiftPMConfig.targetName; + additionalPackages = swiftPMConfig.additionalPackages; + } catch { + if (args.length >= 1) { + appPath = args[0]; + } + + if (args.length >= 2) { + reactNativePath = args[1]; + } + + if (args.length >= 3) { + appXcodeProject = args[2]; + } + + if (args.length >= 4) { + targetName = args[3]; + } + + console.log( + 'Usage: node prepare-app.js [appPath] [reactNativePath] [appXcodeProject] [targetName]', + ); + } + console.log(`Using App path: ${appPath}`); + console.log(`Using React Native path: ${reactNativePath}`); + console.log(`Using App Xcode project: ${appXcodeProject}`); + console.log(`Using Target name: ${targetName}`); + + prepareApp( + appPath, + reactNativePath, + appXcodeProject, + targetName, + additionalPackages, + ) + .then(() => { + console.log( + '\n🎉 All done! Your app is ready for SwiftPM build from source.', + ); + process.exit(0); + }) + .catch(error => { + console.error('\n💥 Preparation failed:', error.message); + process.exit(1); + }); +} + +module.exports = { + prepareApp, + runPodDeintegrate, + runIosPrebuild, + configureAppForSwift, + setBuildFromSource, + createHardlinks, + generateCodegenArtifacts, + prepareHeaders, + fixReactNativePath, + allowNonModularHeaderImport, + integrateSwiftPMPackages, + openXcodeProject, +}; diff --git a/packages/react-native/scripts/swiftpm/update-xcodeproject.js b/packages/react-native/scripts/swiftpm/update-xcodeproject.js new file mode 100644 index 000000000000..10ffcb68f537 --- /dev/null +++ b/packages/react-native/scripts/swiftpm/update-xcodeproject.js @@ -0,0 +1,111 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +/** + * Script to update Xcode project settings for SwiftPM build from source + * + * This script handles updating Xcode project configurations, particularly: + * - REACT_NATIVE_PATH settings + * - Build settings for SwiftPM compatibility + * - Other project-specific configurations + */ + +const { + addLocalSwiftPM, + convertXcodeProjectToJSON, + deintegrateSwiftPM, + updateXcodeProject, +} = require('./xcodeproj-utils'); +const fs = require('fs'); +const path = require('path'); + +/*:: +type SwiftPackage = { + relativePath: string, + targets: Array, +}; +*/ + +/** + * Integrate Swift packages into Xcode project + * @param {string} xcodeProjectPath - Path to the app.xcodeproj file + * @param {Array} packageSwiftObjects - List of PackageSwift objects with relativePath and targets + * @param {string} appTargetName - Name of the app target + */ +function integrateSwiftPackagesInXcode( + xcodeProjectPath /*: string */, + packageSwiftObjects /*: Array */, + appTargetName /*: string */, +) /*: void */ { + // Construct path to project.pbxproj + const projectPbxprojPath = path.join(xcodeProjectPath, 'project.pbxproj'); + + if (!fs.existsSync(projectPbxprojPath)) { + throw new Error(`Project file not found: ${projectPbxprojPath}`); + } + + // Convert to JSON + const xcodeProject = convertXcodeProjectToJSON(projectPbxprojPath); + + // Remove any existing SwiftPM integrations first + deintegrateSwiftPM(xcodeProject); + + // Iterate over PackageSwift objects and execute addLocalSwiftPM + for (const packageSwift of packageSwiftObjects) { + addLocalSwiftPM( + packageSwift.relativePath, + packageSwift.targets, + xcodeProject, + appTargetName, + ); + } + + // Convert back to text format and write to project.pbxproj file + fs.writeFileSync( + projectPbxprojPath, + updateXcodeProject(xcodeProject, projectPbxprojPath), + ); +} + +// CLI usage +if (require.main === module) { + const args = process.argv.slice(2); + + if (args.length < 3) { + console.log( + 'Usage: node update-xcodeproject.js ', + ); + console.log( + 'Example: node update-xcodeproject.js ./MyApp.xcodeproj MyApp \'[{"relativePath":"../react-native","targets":["ReactCommon","React-Core"]}]\'', + ); + process.exit(1); + } + + const xcodeProjectPath = args[0]; + const appTargetName = args[1]; + const packageSwiftObjectsJSON = args[2]; + + try { + const packageSwiftObjects = JSON.parse(packageSwiftObjectsJSON); + integrateSwiftPackagesInXcode( + xcodeProjectPath, + packageSwiftObjects, + appTargetName, + ); + console.log('✅ Successfully integrated Swift packages into Xcode project'); + } catch (error) { + console.error('❌ Error:', error.message); + process.exit(1); + } +} + +module.exports = { + integrateSwiftPackagesInXcode, +}; diff --git a/packages/react-native/scripts/swiftpm/xcodeproj-utils.js b/packages/react-native/scripts/swiftpm/xcodeproj-utils.js new file mode 100644 index 000000000000..3d4d1b252390 --- /dev/null +++ b/packages/react-native/scripts/swiftpm/xcodeproj-utils.js @@ -0,0 +1,800 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + */ + +const {execSync} = require('child_process'); +const crypto = require('crypto'); +const fs = require('fs'); + +/*:: +type XcodeProject = { + // $FlowFixMe[unclear-type] + objects: {[string]: any}, + // $FlowFixMe[unclear-type] + [string]: any, +}; + +type XcodeObject = { + isa: string, + // $FlowFixMe[unclear-type] + [string]: any, +}; + +type SectionToAdd = { + sectionType: string, + replacementText: string, +}; +*/ + +/** + * Generate a random string of 24 HEX characters (capital letters) for Xcode object IDs + * @returns {string} A 24-character hexadecimal string in uppercase + */ +function generateXcodeObjectId() /*: string */ { + return crypto.randomBytes(12).toString('hex').toUpperCase(); +} + +/** + * Convert Xcode project.pbxproj file to JSON format + * @param {string} projectPath - Path to the project.pbxproj file + * @returns {Object} Parsed JSON object of the Xcode project + */ +function convertXcodeProjectToJSON( + projectPath /*: string */, +) /*: XcodeProject */ { + const command = `plutil -convert json -o - "${projectPath}"`; + const jsonOutput = execSync(command, {encoding: 'utf8'}); + return JSON.parse(jsonOutput); +} + +/** + * Reads the project at projectPath and updates it with the changes + * in the xcodeProjectJSON object. + * @param {Object} xcodeProjectJSON - The xcode project JSON object + * @param {string} projectPath - Path to the project.pbxproj file + * @returns {string} Text representation of the project.pbxproj file + */ +function updateXcodeProject( + xcodeProjectJSON /*: XcodeProject */, + projectPath /*: string */, +) /*: string */ { + let textualProject = fs.readFileSync(projectPath, {encoding: 'utf8'}); + + // Group the objects in the JSON by their isa type + const objectsByIsa /*: {[string]: {[string]: XcodeObject}} */ = {}; + for (const [objectId, objectData] of Object.entries( + xcodeProjectJSON.objects, + )) { + const isaType = objectData.isa; + if (!objectsByIsa[isaType]) { + objectsByIsa[isaType] = {}; + } + objectsByIsa[isaType][objectId] = objectData; + } + + // Update the Project.pbxproj file with the sections that needs to be rewritten + textualProject = updateProjectFile( + textualProject, + xcodeProjectJSON, + objectsByIsa, + ); + + // Update PBXProject.packagereference section + textualProject = updatePackageReferenceSection( + textualProject, + xcodeProjectJSON, + objectsByIsa, + ); + + // Update the PBXFrameworksBuildPhase.files array + textualProject = updatePBXFrameworksBuildPhaseFiles( + textualProject, + xcodeProjectJSON, + ); + + return textualProject; +} + +/** + * Remove all existing SwiftPM package references and dependencies from Xcode project + * @param {Object} xcodeProject - The xcode project converted in JSON format + */ +function deintegrateSwiftPM(xcodeProject /*: XcodeProject */) /*: void */ { + const objects = xcodeProject.objects; + const objectsToRemove = []; + + // Step 1: Find all PBXNativeTarget objects and clean up their SwiftPM dependencies + for (const objectId in objects) { + const object = objects[objectId]; + if (object.isa !== 'PBXNativeTarget') continue; + + // Find PBXFrameworksBuildPhase + for (const buildPhaseId of object.buildPhases || []) { + const buildPhaseObject = objects[buildPhaseId]; + if ( + !buildPhaseObject || + buildPhaseObject.isa !== 'PBXFrameworksBuildPhase' + ) + continue; + + const filesToRemove /*: Array */ = []; + + // Check each file in the build phase + for (const fileId of buildPhaseObject.files || []) { + const buildFileObject = objects[fileId]; + if ( + !buildFileObject || + buildFileObject.isa !== 'PBXBuildFile' || + !buildFileObject.productRef + ) + continue; + + const productRefObject = objects[buildFileObject.productRef]; + if ( + !productRefObject || + productRefObject.isa !== 'XCSwiftPackageProductDependency' + ) + continue; + + // Mark for removal: the product dependency, the build file, and remove from files list + objectsToRemove.push(buildFileObject.productRef); + objectsToRemove.push(fileId); + filesToRemove.push(fileId); + } + + // Remove files from the build phase + if (filesToRemove.length > 0) { + buildPhaseObject.files = (buildPhaseObject.files || []).filter( + fileId => !filesToRemove.includes(fileId), + ); + } + } + } + + // Step 2: Find PBXProject and clean up packageReferences + for (const objectId in objects) { + const object = objects[objectId]; + if (object.isa !== 'PBXProject') continue; + + const packageReferencesToRemove = []; + + // Check each package reference + for (const packageRefId of object.packageReferences || []) { + const packageRefObject = objects[packageRefId]; + if ( + !packageRefObject || + packageRefObject.isa !== 'XCLocalSwiftPackageReference' + ) + continue; + + // Mark for removal + objectsToRemove.push(packageRefId); + packageReferencesToRemove.push(packageRefId); + } + + // Remove package references from the project + if (packageReferencesToRemove.length > 0) { + object.packageReferences = (object.packageReferences || []).filter( + refId => !packageReferencesToRemove.includes(refId), + ); + } + + break; + } + + // Step 3: Remove all marked objects + for (const objectId of objectsToRemove) { + delete objects[objectId]; + } + + console.log( + `✓ Removed ${objectsToRemove.length} SwiftPM-related objects from Xcode project`, + ); +} + +/** + * Add local SwiftPM package references and product dependencies to Xcode project + * @param {string} relativePath - The relative path of where the Package.swift is located + * @param {Array} productNames - List of product names exposed by the Package.swift files + * @param {Object} xcodeProject - The xcode project converted in JSON format + * @param {string} targetName - The name of the target to add dependencies to + */ +function addLocalSwiftPM( + relativePath /*: string */, + productNames /*: Array */, + xcodeProject /*: XcodeProject */, + targetName /*: string */, +) /*: void */ { + // For the relative path: create XCLocalSwiftPackageReference + const packageReferenceId = generateXcodeObjectId(); + xcodeProject.objects[packageReferenceId] = { + isa: 'XCLocalSwiftPackageReference', + relativePath: relativePath, + }; + + // Find PBXProject object and update packageReferences + const objects = xcodeProject.objects; + for (const objectId in objects) { + const object = objects[objectId]; + if (object.isa !== 'PBXProject') continue; + + if (!object.packageReferences) { + object.packageReferences = []; + } + object.packageReferences.push(packageReferenceId); + break; + } + + // For each product: create XCSwiftPackageProductDependency and PBXBuildFile + for (const productName of productNames) { + // Generate XcodeID for XCSwiftPackageProductDependency + const productDependencyId = generateXcodeObjectId(); + xcodeProject.objects[productDependencyId] = { + isa: 'XCSwiftPackageProductDependency', + productName: productName, + }; + + // Generate second XcodeID for PBXBuildFile + const buildFileId = generateXcodeObjectId(); + xcodeProject.objects[buildFileId] = { + isa: 'PBXBuildFile', + productRef: productDependencyId, + }; + + // Find PBXNativeTarget with matching name + for (const objectId in objects) { + const object = objects[objectId]; + if (object.isa !== 'PBXNativeTarget' || object.name !== targetName) + continue; + + // Iterate over buildPhases to find PBXFrameworksBuildPhase + for (const buildPhaseId of object.buildPhases) { + const buildPhaseObject = objects[buildPhaseId]; + if ( + !buildPhaseObject || + buildPhaseObject.isa !== 'PBXFrameworksBuildPhase' + ) + continue; + + // Add buildFileId to the files array + if (!buildPhaseObject.files) { + buildPhaseObject.files = []; + } + buildPhaseObject.files.push(buildFileId); + break; + } + break; + } + } +} + +/** + * Update the project.pbxproj file with the sections that needs to be rewritten + * @param {string} textualProject - The textual representation of the Xcode project + * @param {Object} xcodeProjectJSON - The JSON representation of the Xcode project + * @returns {string} Updated textual project the sections that needs to be rewritten. + */ +function updateProjectFile( + textualProject /*: string */, + xcodeProjectJSON /*: XcodeProject */, + objectsByIsa /*: {[string]: {[string]: XcodeObject}} */, +) /*: string */ { + let workingProject = textualProject; + + const sectionsToRewrite /*: {[string]: (objectId: string, objectData: XcodeObject, allObjects: {[string]: XcodeObject}) => string} */ = + { + PBXBuildFile: printPBXBuildFile, + XCLocalSwiftPackageReference: printXCLocalSwiftPackageReference, + XCSwiftPackageProductDependency: printXCSwiftPackageProductDependency, + }; + + // Track which sections exist and which need to be added + const sectionsToAdd = []; + + // Iterate over the sectionsToRewrite and update each section + for (const [sectionType, printFn] of Object.entries(sectionsToRewrite)) { + // Extract objects that correspond to this type + const objectsOfType = objectsByIsa[sectionType] || {}; + + // Sort the objectsOfType by their objectId (keys) alphabetically + const sortedObjectIds = Object.keys(objectsOfType).sort(); + + // Create replacement text using the appropriate print function + let replacementText = ''; + for (const objectId of sortedObjectIds) { + const objectData = objectsOfType[objectId]; + replacementText += printFn( + objectId, + objectData, + xcodeProjectJSON.objects, + ); + } + + // Search for the section in workingProject and replace it + const sectionStartPattern = new RegExp( + `/\\* Begin ${sectionType} section \\*/`, + ); + const sectionEndPattern = new RegExp( + `/\\* End ${sectionType} section \\*/`, + ); + + const startMatch = workingProject.match(sectionStartPattern); + const endMatch = workingProject.match(sectionEndPattern); + + if (startMatch && endMatch) { + const startIndex = startMatch.index + startMatch[0].length; + const endIndex = endMatch.index; + + // Replace the content between the section markers + const beforeSection = workingProject.substring(0, startIndex); + const afterSection = workingProject.substring(endIndex); + + workingProject = beforeSection + '\n' + replacementText + afterSection; + } else if (Object.keys(objectsOfType).length > 0) { + // Section doesn't exist but we have objects to add + sectionsToAdd.push({ + sectionType, + replacementText: `/* Begin ${sectionType} section */\n${replacementText}/* End ${sectionType} section */\n`, + }); + } + } + + // Add missing sections in the correct order if needed + if (sectionsToAdd.length > 0) { + workingProject = addMissingSections(workingProject, sectionsToAdd); + } + + return workingProject; +} + +function updatePackageReferenceSection( + textualProject /*: string */, + xcodeProjectJSON /*: XcodeProject */, + objectsByIsa /*: {[string]: {[string]: XcodeObject}} */, +) /*: string */ { + const lines = textualProject.split('\n'); + const processedLines = []; + let inPBXProjectSection = false; + let inProjectObject = false; + let projectBraceDepth = 0; + let currentProjectObjectId = null; + let packageReferencesInserted = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Check if we're starting the PBXProject section + if (line.includes('/* Begin PBXProject section */')) { + inPBXProjectSection = true; + processedLines.push(line); + continue; + } + + // Check if we're ending the PBXProject section + if (line.includes('/* End PBXProject section */')) { + inPBXProjectSection = false; + processedLines.push(line); + continue; + } + + // If we're in the PBXProject section + if (inPBXProjectSection) { + // Look for project object pattern: "objectId /* Project object */ = {" + const projectMatch = line.match( + /^\s*([A-F0-9]+)\s*\/\*\s*Project object\s*\*\/\s*=\s*\{/, + ); + if (projectMatch) { + inProjectObject = true; + projectBraceDepth = 1; + currentProjectObjectId = projectMatch[1]; + packageReferencesInserted = false; // Reset flag for each project object + processedLines.push(line); + continue; + } + + // If we're in a project object + if (inProjectObject) { + // Track brace depth to know when the project section ends + const openBraces = (line.match(/\{/g) || []).length; + const closeBraces = (line.match(/\}/g) || []).length; + projectBraceDepth += openBraces - closeBraces; + + // Check if we found "packageReferences = (" + if (line.includes('packageReferences = (')) { + packageReferencesInserted = true; // Mark as already present + processedLines.push(line); + + // Generate new packageReferences content + const projectObject = // $FlowFixMe[incompatible-type] + xcodeProjectJSON.objects[currentProjectObjectId]; + if (projectObject && projectObject.packageReferences) { + for (const packageRefId of projectObject.packageReferences) { + const packageRefObject = xcodeProjectJSON.objects[packageRefId]; + if ( + packageRefObject && + packageRefObject.isa === 'XCLocalSwiftPackageReference' + ) { + processedLines.push( + `\t\t\t\t${packageRefId} /* XCLocalSwiftPackageReference "${packageRefObject.relativePath}" */,`, + ); + } + } + } + + // Skip lines until we find the closing ");" + i++; + while (i < lines.length && !lines[i].includes(');')) { + i++; + } + // Add the closing ");" line + if (i < lines.length) { + processedLines.push(lines[i]); + } + continue; + } + + // Check if we need to insert packageReferences property + if ( + projectBraceDepth === 1 && + !packageReferencesInserted && + !line.includes('packageReferences') && + line.trim().endsWith(';') + ) { + // Check if this is a property line we should insert packageReferences before + // Insert packageReferences in alphabetical order (after 'mainGroup' but before 'projectDirPath') + const propertyMatch = line.match(/^\s*(\w+)\s*=/); + if (propertyMatch) { + const propertyName = propertyMatch[1]; + + // Insert packageReferences before properties that come after 'p' alphabetically + if (propertyName > 'packageReferences') { + const projectObject = // $FlowFixMe[incompatible-type] + xcodeProjectJSON.objects[currentProjectObjectId]; + if ( + projectObject && + projectObject.packageReferences && + projectObject.packageReferences.length > 0 + ) { + processedLines.push('\t\t\tpackageReferences = ('); + for (const packageRefId of projectObject.packageReferences) { + const packageRefObject = + xcodeProjectJSON.objects[packageRefId]; + if ( + packageRefObject && + packageRefObject.isa === 'XCLocalSwiftPackageReference' + ) { + processedLines.push( + `\t\t\t\t${packageRefId} /* XCLocalSwiftPackageReference "${packageRefObject.relativePath}" */,`, + ); + } + } + processedLines.push('\t\t\t);'); + packageReferencesInserted = true; // Mark as inserted + } + } + } + } + + // If we're at the end of the project section + if (projectBraceDepth === 0) { + inProjectObject = false; + currentProjectObjectId = null; + packageReferencesInserted = false; + } + + processedLines.push(line); + continue; + } + } + + // Default: just add the line as-is + processedLines.push(line); + } + + return processedLines.join('\n'); +} + +/** + * Print PBXBuildFile object + * @param {string} objectId - The object ID + * @param {Object} objectData - The object data + * @param {Object} allObjects - All objects for reference lookup + * @returns {string} Formatted string for this object type + */ +function printPBXBuildFile( + objectId /*: string */, + objectData /*: XcodeObject */, + allObjects /*: {[string]: XcodeObject} */, +) /*: string */ { + // Handle productRef case for Swift Package dependencies + if (objectData.productRef) { + const productRefObject = allObjects[objectData.productRef]; + const productName = productRefObject + ? productRefObject.productName + : 'Unknown'; + return `\t\t${objectId} /* ${productName} in Frameworks */ = {isa = PBXBuildFile; productRef = ${objectData.productRef} /* ${productName} */; };\n`; + } + + // Handle fileRef case for regular files + const referencedFile = allObjects[objectData.fileRef]; + const filename = referencedFile + ? referencedFile.name || referencedFile.path || 'Unknown' + : 'Unknown'; + + // Determine the type by searching build phases + let type = 'Unknown'; + for (const [, phaseObject] of Object.entries(allObjects)) { + if (phaseObject.files && phaseObject.files.includes(objectId)) { + // Check if the isa property ends up with "BuildPhase" + if (phaseObject.isa.endsWith('BuildPhase')) { + // remove the PBX prefix and the BuildPhase suffix + type = phaseObject.isa.substring(3, phaseObject.isa.length - 10); + break; + } + } + } + + // Format the output as a single line + return `\t\t${objectId} /* ${filename} in ${type} */ = {isa = PBXBuildFile; fileRef = ${objectData.fileRef} /* ${filename} */; };\n`; +} + +/** + * Print XCLocalSwiftPackageReference object + * @param {string} objectId - The object ID + * @param {Object} objectData - The object data + * @param {Object} allObjects - All objects for reference lookup + * @returns {string} Formatted string for this object type + */ +function printXCLocalSwiftPackageReference( + objectId /*: string */, + objectData /*: XcodeObject */, + allObjects /*: {[string]: XcodeObject} */, +) /*: string */ { + const relativePath = objectData.relativePath; + + // Escape path with quotes if it contains spaces + const escapedPath = relativePath.includes(' ') + ? `"${relativePath}"` + : relativePath; + + return `\t\t${objectId} /* XCLocalSwiftPackageReference "${relativePath}" */ = { +\t\t\tisa = XCLocalSwiftPackageReference; +\t\t\trelativePath = ${escapedPath}; +\t\t}; +`; +} + +/** + * Print XCSwiftPackageProductDependency object + * @param {string} objectId - The object ID + * @param {Object} objectData - The object data + * @param {Object} allObjects - All objects for reference lookup + * @returns {string} Formatted string for this object type + */ +function printXCSwiftPackageProductDependency( + objectId /*: string */, + objectData /*: XcodeObject */, + allObjects /*: {[string]: XcodeObject} */, +) /*: string */ { + const productName = objectData.productName; + + return `\t\t${objectId} /* ${productName} */ = { +\t\t\tisa = XCSwiftPackageProductDependency; +\t\t\tproductName = ${productName}; +\t\t}; +`; +} + +function printFilesForBuildPhase( + objectId /*: string */, + objectData /*: XcodeObject */, + allObjects /*: {[string]: XcodeObject} */, +) /*: string */ { + // Get the product name from the productRef in the PBXBuildFile + let productName = 'Unknown'; + + if (objectData.productRef) { + const productRefObject = allObjects[objectData.productRef]; + if (productRefObject && productRefObject.productName) { + productName = productRefObject.productName; + } + } else if (objectData.fileRef) { + const fileRefObject = allObjects[objectData.fileRef]; + if (fileRefObject) { + productName = fileRefObject.name || fileRefObject.path || 'Unknown'; + } + } + + return `\t\t\t\t${objectId} /* ${productName} in Frameworks */,\n`; +} + +/** + * Add missing sections to the textual project in the correct order + * @param {string} textualProject - The textual representation of the Xcode project + * @param {Array} sectionsToAdd - Array of sections to add with their content + * @returns {string} Updated textual project with new sections added + */ +function addMissingSections( + textualProject /*: string */, + sectionsToAdd /*: Array */, +) /*: string */ { + // Define the order of sections - PBXBuildFile first, then XCLocalSwiftPackageReference, then XCSwiftPackageProductDependency + const sectionOrder = [ + 'PBXBuildFile', + 'XCLocalSwiftPackageReference', + 'XCSwiftPackageProductDependency', + ]; + + // Sort sections according to the defined order + sectionsToAdd.sort((a, b) => { + const indexA = sectionOrder.indexOf(a.sectionType); + const indexB = sectionOrder.indexOf(b.sectionType); + return indexA - indexB; + }); + + // Find the insertion points for each section type + const lines = textualProject.split('\n'); + let insertionIndex = -1; + + for (const sectionToAdd of sectionsToAdd) { + const {sectionType, replacementText} = sectionToAdd; + + if (sectionType === 'PBXBuildFile') { + // PBXBuildFile should be first in the objects array + // Find the first existing section after "objects = {" + for (let i = 0; i < lines.length; i++) { + if (lines[i].includes('objects = {')) { + insertionIndex = i + 1; + break; + } + } + } else if (sectionType === 'XCLocalSwiftPackageReference') { + // Should be second-last before rootObject + // Find the rootObject line and go back to find a good insertion point + for (let i = lines.length - 1; i >= 0; i--) { + if (lines[i].includes('rootObject =')) { + insertionIndex = i - 1; + break; + } + } + } else if (sectionType === 'XCSwiftPackageProductDependency') { + // Should be last before rootObject + for (let i = lines.length - 1; i >= 0; i--) { + if (lines[i].includes('rootObject =')) { + insertionIndex = i - 1; + break; + } + } + } + + // Insert the section at the determined index + if (insertionIndex !== -1) { + lines.splice(insertionIndex, 0, replacementText); + // Update insertion index for subsequent sections + insertionIndex += replacementText.split('\n').length; + } + } + + return lines.join('\n'); +} + +/** + * Update the PBXFrameworksBuildPhase.files array in the textual project + * @param {string} textualProject - The textual representation of the Xcode project + * @param {Object} xcodeProjectJSON - The JSON representation of the Xcode project + * @returns {string} Updated textual project with new PBXFrameworksBuildPhase.files content + */ +function updatePBXFrameworksBuildPhaseFiles( + textualProject /*: string */, + xcodeProjectJSON /*: XcodeProject */, +) /*: string */ { + const lines = textualProject.split('\n'); + const processedLines = []; + let inPBXFrameworksBuildPhase = false; + let inFrameworksSection = false; + let frameworksBraceDepth = 0; + let currentFrameworksBuildPhaseId = null; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + + // Check if we're starting the PBXFrameworksBuildPhase section + if (line.includes('/* Begin PBXFrameworksBuildPhase section */')) { + inPBXFrameworksBuildPhase = true; + processedLines.push(line); + continue; + } + + // Check if we're ending the PBXFrameworksBuildPhase section + if (line.includes('/* End PBXFrameworksBuildPhase section */')) { + inPBXFrameworksBuildPhase = false; + processedLines.push(line); + continue; + } + + // If we're in the PBXFrameworksBuildPhase section + if (inPBXFrameworksBuildPhase) { + // Look for framework build phase pattern: "objectId /* Frameworks */ = {" + const frameworksMatch = line.match( + /^\s*([A-F0-9]+)\s*\/\*\s*Frameworks\s*\*\/\s*=\s*\{/, + ); + if (frameworksMatch) { + inFrameworksSection = true; + frameworksBraceDepth = 1; + currentFrameworksBuildPhaseId = frameworksMatch[1]; + processedLines.push(line); + continue; + } + + // If we're in a frameworks section + if (inFrameworksSection) { + // Track brace depth to know when the frameworks section ends + const openBraces = (line.match(/\{/g) || []).length; + const closeBraces = (line.match(/\}/g) || []).length; + frameworksBraceDepth += openBraces - closeBraces; + + // Check if we found "files = (" + if (line.includes('files = (')) { + processedLines.push(line); + + // Generate new files content + + const frameworksBuildPhase = // $FlowFixMe[incompatible-type] + xcodeProjectJSON.objects[currentFrameworksBuildPhaseId]; + if (frameworksBuildPhase && frameworksBuildPhase.files) { + for (const fileId of frameworksBuildPhase.files) { + const buildFileObject = xcodeProjectJSON.objects[fileId]; + if (buildFileObject) { + processedLines.push( + printFilesForBuildPhase( + fileId, + buildFileObject, + xcodeProjectJSON.objects, + ), + ); + } + } + } + + // Skip lines until we find the closing ");" + i++; + while (i < lines.length && !lines[i].includes(');')) { + i++; + } + // Add the closing ");" line + if (i < lines.length) { + processedLines.push(lines[i]); + } + continue; + } + + // If we're at the end of the frameworks section + if (frameworksBraceDepth === 0) { + inFrameworksSection = false; + currentFrameworksBuildPhaseId = null; + } + + processedLines.push(line); + continue; + } + } + + // Default: just add the line as-is + processedLines.push(line); + } + + return processedLines.join('\n'); +} + +module.exports = { + convertXcodeProjectToJSON, + updateXcodeProject, + deintegrateSwiftPM, + addLocalSwiftPM, +}; diff --git a/packages/rn-tester/NativeComponentExample/ios/RNTMyNativeViewComponentView.mm b/packages/rn-tester/NativeComponentExample/ios/RNTMyNativeViewComponentView.mm index 2eb7bff28e67..c756f1c1590d 100644 --- a/packages/rn-tester/NativeComponentExample/ios/RNTMyNativeViewComponentView.mm +++ b/packages/rn-tester/NativeComponentExample/ios/RNTMyNativeViewComponentView.mm @@ -13,8 +13,6 @@ #import #import -#import "RCTFabricComponentsPlugins.h" - using namespace facebook::react; @interface RNTMyNativeViewComponentView () diff --git a/packages/rn-tester/NativeCxxModuleExample/module.modulemap b/packages/rn-tester/NativeCxxModuleExample/module.modulemap new file mode 100644 index 000000000000..49b9e4f7581b --- /dev/null +++ b/packages/rn-tester/NativeCxxModuleExample/module.modulemap @@ -0,0 +1,5 @@ +framework module NativeCxxModuleExample { + header "NativeCxxModuleExample.h" + export * + module * { export * } +} diff --git a/packages/rn-tester/Package.swift b/packages/rn-tester/Package.swift new file mode 100644 index 000000000000..8e24e12ec0c8 --- /dev/null +++ b/packages/rn-tester/Package.swift @@ -0,0 +1,62 @@ +// swift-tools-version: 6.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "RNTesterModules", + platforms: [.iOS(.v15), .macCatalyst(SupportedPlatform.MacCatalystVersion.v13)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "NativeCxxModuleExample", + targets: ["NativeCxxModuleExample"] + ), + .library( + name: "NativeComponentExample", + targets: ["NativeComponentExample"] + ), + ], + dependencies: [ + .package(name: "React", path: "../react-native"), + .package(name: "ReactCodegen", path: "./build/generated/ios"), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + .target( + name: "NativeCxxModuleExample", + dependencies: ["React", "ReactCodegen"], + path: "NativeCxxModuleExample", + exclude: ["tests/", "CMakeLists.txt", "NativeCxxModuleExample.js", "module.modulemap", "NativeCxxModuleExample.podspec"], + publicHeadersPath: ".", + // cSettings: [ + // .headerSearchPath("headers") + // ], + cxxSettings: [ + // .headerSearchPath("headers"), + .unsafeFlags(["-std=c++20"]), + ], + linkerSettings: [ + .linkedFramework("Foundation") + ] + ), + .target( + name: "NativeComponentExample", + dependencies: ["React", "ReactCodegen"], + path: "NativeComponentExample", + exclude: ["js/", "MyNativeView.podspec"], + publicHeadersPath: "ios", + // cSettings: [ + // .headerSearchPath("headers") + // ], + cxxSettings: [ + // .headerSearchPath("headers"), + .unsafeFlags(["-std=c++20"]), + ], + linkerSettings: [ + .linkedFramework("Foundation") + ] + ), + ] +) diff --git a/packages/rn-tester/RNTester/AppDelegate.mm b/packages/rn-tester/RNTester/AppDelegate.mm index cf02164f8945..87317ccd7651 100644 --- a/packages/rn-tester/RNTester/AppDelegate.mm +++ b/packages/rn-tester/RNTester/AppDelegate.mm @@ -17,9 +17,11 @@ #import -#import #ifndef RN_DISABLE_OSS_PLUGIN_HEADER #import +#import +#else +#import #endif #if __has_include() diff --git a/packages/rn-tester/package.json b/packages/rn-tester/package.json index 9961db5e82b5..80ba1dc00377 100644 --- a/packages/rn-tester/package.json +++ b/packages/rn-tester/package.json @@ -23,7 +23,8 @@ "e2e-build-android": "../../gradlew :packages:rn-tester:android:app:installRelease -PreactNativeArchitectures=arm64-v8a", "e2e-build-ios": "./scripts/maestro-build-ios.sh", "e2e-test-android": "maestro test .maestro/ -e APP_ID=com.facebook.react.uiapp", - "e2e-test-ios": "./scripts/maestro-test-ios.sh" + "e2e-test-ios": "./scripts/maestro-test-ios.sh", + "prepare-with-swiftpm": "node ../../packages/react-native/scripts/swiftpm/prepare-app . ../react-native RNTesterPods.xcodeproj RNTester" }, "dependencies": { "@react-native/new-app-screen": "0.82.0-main", diff --git a/packages/rn-tester/swiftpm.config.js b/packages/rn-tester/swiftpm.config.js new file mode 100644 index 000000000000..5a1e8a38b173 --- /dev/null +++ b/packages/rn-tester/swiftpm.config.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @noflow + */ + +'use strict'; + +const path = require('path'); + +const config = { + appPath: path.resolve(__dirname, '.'), + reactNativePath: path.resolve( + __dirname, + '..', + '..', + 'packages', + 'react-native', + ), + appXcodeProject: 'RNTesterPods.xcodeproj', + targetName: 'RNTester', + additionalPackages: [ + { + relativePath: '../rn-tester', + targets: ['NativeComponentExample', 'NativeCxxModuleExample'], + }, + { + relativePath: '../react-native/Libraries', + targets: ['PushNotification'], + }, + ], +}; + +module.exports = config; diff --git a/private/helloworld/ios/HelloWorld.xcodeproj/+project.pbxproj b/private/helloworld/ios/HelloWorld.xcodeproj/+project.pbxproj new file mode 100644 index 000000000000..8f75f0049cf6 --- /dev/null +++ b/private/helloworld/ios/HelloWorld.xcodeproj/+project.pbxproj @@ -0,0 +1,618 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 60; + objects = { + +/* Begin PBXBuildFile section */ + 00E356F31AD99517003FC87E /* HelloWorldTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* HelloWorldTests.m */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 3B2B49685FACF89CCE4CBB8C /* React in Frameworks */ = {isa = PBXBuildFile; productRef = 2AC85BBE9E8CE327E50A5268 /* React */; }; + 3EA36F0CA1DD66CA4BFCEA10 /* ReactCodegen in Frameworks */ = {isa = PBXBuildFile; productRef = EB3838A7F80F0405E2ACB967 /* ReactCodegen */; }; + 64740A5856D6F422D67540F9 /* ReactAppDependencyProvider in Frameworks */ = {isa = PBXBuildFile; productRef = 507C9330E5CE725E42B5B5F8 /* ReactAppDependencyProvider */; }; + 6EA01F72FAC10D00AECACF94 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 0EC7AB76F90EED035707BA4E /* PrivacyInfo.xcprivacy */; }; + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; + CDA0ED1A2D0B2D810079F561 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CDA0ED192D0B2D810079F561 /* AppDelegate.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = HelloWorld; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00E356EE1AD99517003FC87E /* HelloWorldTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = HelloWorldTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + 0EC7AB76F90EED035707BA4E /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = HelloWorld/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* HelloWorld.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HelloWorld.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 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 = ""; }; + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = HelloWorld/PrivacyInfo.xcprivacy; sourceTree = ""; }; + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = HelloWorld/LaunchScreen.storyboard; sourceTree = ""; }; + CDA0ED192D0B2D810079F561 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = HelloWorld/AppDelegate.swift; sourceTree = ""; }; + ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3B2B49685FACF89CCE4CBB8C /* React in Frameworks */, + 3EA36F0CA1DD66CA4BFCEA10 /* ReactCodegen in Frameworks */, + 64740A5856D6F422D67540F9 /* ReactAppDependencyProvider in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00E356EF1AD99517003FC87E /* HelloWorldTests */ = { + isa = PBXGroup; + children = ( + 00E356F21AD99517003FC87E /* HelloWorldTests.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, + ); + path = HelloWorldTests; + sourceTree = ""; + }; + 00E356F01AD99517003FC87E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 00E356F11AD99517003FC87E /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* HelloWorld */ = { + isa = PBXGroup; + children = ( + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, + 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */, + 0EC7AB76F90EED035707BA4E /* PrivacyInfo.xcprivacy */, + CDA0ED192D0B2D810079F561 /* AppDelegate.swift */, + ); + name = HelloWorld; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + ); + name = Libraries; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* HelloWorld */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* HelloWorldTests */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + BBD78D7AC51CEA395F1C20DB /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* HelloWorld.app */, + 00E356EE1AD99517003FC87E /* HelloWorldTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + BBD78D7AC51CEA395F1C20DB /* Pods */ = { + isa = PBXGroup; + children = ( + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* HelloWorldTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "HelloWorldTests" */; + buildPhases = ( + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = HelloWorldTests; + productName = HelloWorldTests; + productReference = 00E356EE1AD99517003FC87E /* HelloWorldTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 13B07F861A680F5B00A75B9A /* HelloWorld */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "HelloWorld" */; + buildPhases = ( + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HelloWorld; + productName = HelloWorld; + productReference = 13B07F961A680F5B00A75B9A /* HelloWorld.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1210; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1610; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "HelloWorld" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + packageReferences = ( + CA6D0B9ED87ABF13D357B004 /* XCLocalSwiftPackageReference "../../../packages/react-native" */, + 0992998BF64278B94125CD6F /* XCLocalSwiftPackageReference "build/generated/ios" */, + ); + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* HelloWorld */, + 00E356ED1AD99517003FC87E /* HelloWorldTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + 6EA01F72FAC10D00AECACF94 /* PrivacyInfo.xcprivacy in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/.xcode.env", + "$(SRCROOT)/../.react-native.config", + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nexport CONFIG_JSON=$(sed -e \"s|HELLOWORLD_PATH|$(realpath \"${SRCROOT}/../\")|g\" \"${SRCROOT}/../.react-native.config\")\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* HelloWorldTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CDA0ED1A2D0B2D810079F561 /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* HelloWorld */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = HelloWorldTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + REACT_NATIVE_PATH = "${PROJECT_DIR}/../../../packages/react-native"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HelloWorld.app/HelloWorld"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COPY_PHASE_STRIP = NO; + INFOPLIST_FILE = HelloWorldTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + REACT_NATIVE_PATH = "${PROJECT_DIR}/../../../packages/react-native"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HelloWorld.app/HelloWorld"; + }; + name = Release; + }; + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = HelloWorld/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = HelloWorld; + REACT_NATIVE_PATH = "${PROJECT_DIR}/../../../packages/react-native"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = HelloWorld/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = HelloWorld; + REACT_NATIVE_PATH = "${PROJECT_DIR}/../../../packages/react-native"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", + ); + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + OTHER_LDFLAGS = "$(inherited)"; + REACT_NATIVE_PATH = "${PROJECT_DIR}/../../../packages/react-native"; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG"; + USE_HERMES = true; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++20"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = ""; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx", + "${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers", + "${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios", + ); + IPHONEOS_DEPLOYMENT_TARGET = 15.1; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + "-DFOLLY_CFG_NO_COROUTINES=1", + "-DFOLLY_HAVE_CLOCK_GETTIME=1", + ); + OTHER_LDFLAGS = "$(inherited)"; + REACT_NATIVE_PATH = "${PROJECT_DIR}/../../../packages/react-native"; + SDKROOT = iphoneos; + USE_HERMES = true; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "HelloWorldTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "HelloWorld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "HelloWorld" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + 0992998BF64278B94125CD6F /* XCLocalSwiftPackageReference "build/generated/ios" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = build/generated/ios; + }; + CA6D0B9ED87ABF13D357B004 /* XCLocalSwiftPackageReference "../../../packages/react-native" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../../../packages/react-native"; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 2AC85BBE9E8CE327E50A5268 /* React */ = { + isa = XCSwiftPackageProductDependency; + productName = React; + }; + 507C9330E5CE725E42B5B5F8 /* ReactAppDependencyProvider */ = { + isa = XCSwiftPackageProductDependency; + productName = ReactAppDependencyProvider; + }; + EB3838A7F80F0405E2ACB967 /* ReactCodegen */ = { + isa = XCSwiftPackageProductDependency; + productName = ReactCodegen; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/private/helloworld/ios/HelloWorld.xcodeproj/_project.pbxproj b/private/helloworld/ios/HelloWorld.xcodeproj/_project.pbxproj new file mode 100644 index 000000000000..e26b17b3f4bd --- /dev/null +++ b/private/helloworld/ios/HelloWorld.xcodeproj/_project.pbxproj @@ -0,0 +1 @@ +{"classes":{},"objectVersion":"54","archiveVersion":"1","objects":{"ED297162215061F000B7C4FE":{"path":"System/Library/Frameworks/JavaScriptCore.framework","isa":"PBXFileReference","name":"JavaScriptCore.framework","lastKnownFileType":"wrapper.framework","sourceTree":"SDKROOT"},"83CBB9F61A601CBA00E9B192":{"children":["13B07FAE1A68108700A75B9A","832341AE1AAA6A7D00B99B32","00E356EF1AD99517003FC87E","83CBBA001A601CBA00E9B192","2D16E6871FA4F8E400B85C8A","BBD78D7AC51CEA395F1C20DB"],"isa":"PBXGroup","sourceTree":"","indentWidth":"2","tabWidth":"2","usesTabs":"0"},"6EA01F72FAC10D00AECACF94":{"isa":"PBXBuildFile","fileRef":"0EC7AB76F90EED035707BA4E"},"13B07FBF1A68108700A75B9A":{"isa":"PBXBuildFile","fileRef":"13B07FB51A68108700A75B9A"},"00E356F51AD99517003FC87E":{"isa":"PBXTargetDependency","target":"13B07F861A680F5B00A75B9A","targetProxy":"00E356F41AD99517003FC87E"},"00E356F01AD99517003FC87E":{"isa":"PBXGroup","name":"Supporting Files","children":["00E356F11AD99517003FC87E"],"sourceTree":""},"83CBBA201A601CBA00E9B192":{"isa":"XCBuildConfiguration","buildSettings":{"CLANG_WARN_RANGE_LOOP_ANALYSIS":"YES","CLANG_WARN_INFINITE_RECURSION":"YES","ONLY_ACTIVE_ARCH":"YES","REACT_NATIVE_PATH":"${PROJECT_DIR}/../../../packages/react-native","GCC_WARN_UNUSED_VARIABLE":"YES","CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING":"YES","GCC_SYMBOLS_PRIVATE_EXTERN":"NO","COPY_PHASE_STRIP":"NO","CLANG_WARN_BOOL_CONVERSION":"YES","CLANG_WARN_ENUM_CONVERSION":"YES","CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS":"YES","CLANG_WARN_COMMA":"YES","SWIFT_ACTIVE_COMPILATION_CONDITIONS":"$(inherited) DEBUG","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"","CLANG_WARN_INT_CONVERSION":"YES","ENABLE_STRICT_OBJC_MSGSEND":"YES","CLANG_WARN_SUSPICIOUS_MOVE":"YES","CLANG_WARN_EMPTY_BODY":"YES","SDKROOT":"iphoneos","MTL_ENABLE_DEBUG_INFO":"YES","OTHER_LDFLAGS":["$(inherited)"," "],"IPHONEOS_DEPLOYMENT_TARGET":"15.1","USE_HERMES":"true","LD_RUNPATH_SEARCH_PATHS":["/usr/lib/swift","$(inherited)"],"CLANG_CXX_LIBRARY":"libc++","CLANG_ENABLE_OBJC_ARC":"YES","GCC_WARN_UNINITIALIZED_AUTOS":"YES_AGGRESSIVE","GCC_C_LANGUAGE_STANDARD":"gnu99","CLANG_ENABLE_MODULES":"YES","OTHER_CPLUSPLUSFLAGS":["$(OTHER_CFLAGS)","-DFOLLY_NO_CONFIG","-DFOLLY_MOBILE=1","-DFOLLY_USE_LIBCPP=1","-DFOLLY_CFG_NO_COROUTINES=1","-DFOLLY_HAVE_CLOCK_GETTIME=1"],"ALWAYS_SEARCH_USER_PATHS":"NO","CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED":"YES","ENABLE_TESTABILITY":"YES","CLANG_WARN_DIRECT_OBJC_ISA_USAGE":"YES_ERROR","CLANG_WARN_CONSTANT_CONVERSION":"YES","GCC_NO_COMMON_BLOCKS":"YES","HEADER_SEARCH_PATHS":["$(inherited)","${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers","${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core","${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers","${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios","${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx","${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers","${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers","${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios"],"CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF":"YES","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"iPhone Developer","LIBRARY_SEARCH_PATHS":["\"$(SDKROOT)/usr/lib/swift\"","\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"","\"$(inherited)\""],"CLANG_WARN_OBJC_ROOT_CLASS":"YES_ERROR","GCC_WARN_64_TO_32_BIT_CONVERSION":"YES","CLANG_CXX_LANGUAGE_STANDARD":"c++20","GCC_DYNAMIC_NO_PIC":"NO","CLANG_WARN__DUPLICATE_METHOD_MATCH":"YES","GCC_WARN_UNDECLARED_SELECTOR":"YES","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"YES","GCC_OPTIMIZATION_LEVEL":"0","CLANG_WARN_STRICT_PROTOTYPES":"YES","GCC_WARN_ABOUT_RETURN_TYPE":"YES_ERROR","GCC_WARN_UNUSED_FUNCTION":"YES","CLANG_WARN_UNREACHABLE_CODE":"YES","CLANG_WARN_NON_LITERAL_NULL_CONVERSION":"YES","GCC_PREPROCESSOR_DEFINITIONS":["DEBUG=1","$(inherited)"],"CLANG_WARN_OBJC_LITERAL_CONVERSION":"YES"},"name":"Debug"},"13B07FB61A68108700A75B9A":{"path":"HelloWorld/Info.plist","isa":"PBXFileReference","lastKnownFileType":"text.plist.xml","name":"Info.plist","sourceTree":"","fileEncoding":"4"},"81AB9BB72411601600AC10FF":{"path":"HelloWorld/LaunchScreen.storyboard","isa":"PBXFileReference","lastKnownFileType":"file.storyboard","name":"LaunchScreen.storyboard","sourceTree":"","fileEncoding":"4"},"00E356EF1AD99517003FC87E":{"path":"HelloWorldTests","isa":"PBXGroup","children":["00E356F21AD99517003FC87E","00E356F01AD99517003FC87E"],"sourceTree":""},"83CBB9F71A601CBA00E9B192":{"buildConfigurationList":"83CBB9FA1A601CBA00E9B192","targets":["13B07F861A680F5B00A75B9A","00E356ED1AD99517003FC87E"],"developmentRegion":"en","knownRegions":["en","Base"],"isa":"PBXProject","compatibilityVersion":"Xcode 12.0","productRefGroup":"83CBBA001A601CBA00E9B192","projectDirPath":"","attributes":{"LastUpgradeCheck":"1210","TargetAttributes":{"00E356ED1AD99517003FC87E":{"TestTargetID":"13B07F861A680F5B00A75B9A","CreatedOnToolsVersion":"6.2"},"13B07F861A680F5B00A75B9A":{"LastSwiftMigration":"1610"}}},"hasScannedForEncodings":"0","projectRoot":"","mainGroup":"83CBB9F61A601CBA00E9B192","packageReferences":["25D10417CDEB6879A4F00877","EE1CB16CFB4619F31C5294CE"]},"00E356EA1AD99517003FC87E":{"isa":"PBXSourcesBuildPhase","buildActionMask":"2147483647","files":["00E356F31AD99517003FC87E"],"runOnlyForDeploymentPostprocessing":"0"},"13B07F941A680F5B00A75B9A":{"isa":"XCBuildConfiguration","buildSettings":{"LD_RUNPATH_SEARCH_PATHS":["$(inherited)","@executable_path/Frameworks"],"ENABLE_BITCODE":"NO","CURRENT_PROJECT_VERSION":"1","MARKETING_VERSION":"1.0","CLANG_ENABLE_MODULES":"YES","INFOPLIST_FILE":"HelloWorld/Info.plist","VERSIONING_SYSTEM":"apple-generic","SWIFT_OPTIMIZATION_LEVEL":"-Onone","PRODUCT_NAME":"HelloWorld","SWIFT_VERSION":"5.0","ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon","OTHER_LDFLAGS":["$(inherited)","-ObjC","-lc++"],"REACT_NATIVE_PATH":"${PROJECT_DIR}/../../../packages/react-native","PRODUCT_BUNDLE_IDENTIFIER":"org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"},"name":"Debug"},"13B07F8E1A680F5B00A75B9A":{"isa":"PBXResourcesBuildPhase","buildActionMask":"2147483647","files":["81AB9BB82411601600AC10FF","13B07FBF1A68108700A75B9A","6EA01F72FAC10D00AECACF94"],"runOnlyForDeploymentPostprocessing":"0"},"13B07F951A680F5B00A75B9A":{"isa":"XCBuildConfiguration","buildSettings":{"LD_RUNPATH_SEARCH_PATHS":["$(inherited)","@executable_path/Frameworks"],"CURRENT_PROJECT_VERSION":"1","MARKETING_VERSION":"1.0","CLANG_ENABLE_MODULES":"YES","INFOPLIST_FILE":"HelloWorld/Info.plist","VERSIONING_SYSTEM":"apple-generic","PRODUCT_NAME":"HelloWorld","ASSETCATALOG_COMPILER_APPICON_NAME":"AppIcon","SWIFT_VERSION":"5.0","OTHER_LDFLAGS":["$(inherited)","-ObjC","-lc++"],"PRODUCT_BUNDLE_IDENTIFIER":"org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)","REACT_NATIVE_PATH":"${PROJECT_DIR}/../../../packages/react-native"},"name":"Release"},"832341AE1AAA6A7D00B99B32":{"isa":"PBXGroup","name":"Libraries","children":[],"sourceTree":""},"CDA0ED1A2D0B2D810079F561":{"isa":"PBXBuildFile","fileRef":"CDA0ED192D0B2D810079F561"},"00E356F61AD99517003FC87E":{"isa":"XCBuildConfiguration","buildSettings":{"IPHONEOS_DEPLOYMENT_TARGET":"15.1","TEST_HOST":"$(BUILT_PRODUCTS_DIR)/HelloWorld.app/HelloWorld","LD_RUNPATH_SEARCH_PATHS":["$(inherited)","@executable_path/Frameworks","@loader_path/Frameworks"],"INFOPLIST_FILE":"HelloWorldTests/Info.plist","PRODUCT_BUNDLE_IDENTIFIER":"org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)","OTHER_LDFLAGS":["-ObjC","-lc++","$(inherited)"],"REACT_NATIVE_PATH":"${PROJECT_DIR}/../../../packages/react-native","BUNDLE_LOADER":"$(TEST_HOST)","PRODUCT_NAME":"$(TARGET_NAME)","GCC_PREPROCESSOR_DEFINITIONS":["DEBUG=1","$(inherited)"]},"name":"Debug"},"00E356F11AD99517003FC87E":{"path":"Info.plist","isa":"PBXFileReference","lastKnownFileType":"text.plist.xml","sourceTree":""},"83CBBA211A601CBA00E9B192":{"isa":"XCBuildConfiguration","buildSettings":{"CLANG_WARN_SUSPICIOUS_MOVE":"YES","GCC_WARN_UNDECLARED_SELECTOR":"YES","CLANG_ENABLE_OBJC_ARC":"YES","CLANG_WARN_DIRECT_OBJC_ISA_USAGE":"YES_ERROR","ENABLE_NS_ASSERTIONS":"NO","CLANG_WARN__DUPLICATE_METHOD_MATCH":"YES","EXCLUDED_ARCHS[sdk=iphonesimulator*]":"","CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF":"YES","CLANG_WARN_INFINITE_RECURSION":"YES","CODE_SIGN_IDENTITY[sdk=iphoneos*]":"iPhone Developer","SDKROOT":"iphoneos","CLANG_CXX_LANGUAGE_STANDARD":"c++20","CLANG_WARN_BOOL_CONVERSION":"YES","CLANG_WARN_UNREACHABLE_CODE":"YES","ENABLE_STRICT_OBJC_MSGSEND":"YES","CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING":"YES","CLANG_WARN_STRICT_PROTOTYPES":"YES","CLANG_WARN_ENUM_CONVERSION":"YES","CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER":"YES","GCC_WARN_64_TO_32_BIT_CONVERSION":"YES","OTHER_LDFLAGS":["$(inherited)"," "],"USE_HERMES":"true","CLANG_WARN_EMPTY_BODY":"YES","GCC_WARN_UNINITIALIZED_AUTOS":"YES_AGGRESSIVE","GCC_WARN_ABOUT_RETURN_TYPE":"YES_ERROR","REACT_NATIVE_PATH":"${PROJECT_DIR}/../../../packages/react-native","OTHER_CPLUSPLUSFLAGS":["$(OTHER_CFLAGS)","-DFOLLY_NO_CONFIG","-DFOLLY_MOBILE=1","-DFOLLY_USE_LIBCPP=1","-DFOLLY_CFG_NO_COROUTINES=1","-DFOLLY_HAVE_CLOCK_GETTIME=1"],"CLANG_WARN_COMMA":"YES","GCC_C_LANGUAGE_STANDARD":"gnu99","GCC_WARN_UNUSED_VARIABLE":"YES","CLANG_WARN_RANGE_LOOP_ANALYSIS":"YES","CLANG_ENABLE_MODULES":"YES","GCC_NO_COMMON_BLOCKS":"YES","CLANG_WARN_INT_CONVERSION":"YES","ALWAYS_SEARCH_USER_PATHS":"NO","CLANG_WARN_OBJC_ROOT_CLASS":"YES_ERROR","COPY_PHASE_STRIP":"YES","CLANG_WARN_CONSTANT_CONVERSION":"YES","CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS":"YES","HEADER_SEARCH_PATHS":["$(inherited)","${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers","${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core","${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers","${PODS_CONFIGURATION_BUILD_DIR}/ReactCommon-Samples/ReactCommon_Samples.framework/Headers/platform/ios","${PODS_CONFIGURATION_BUILD_DIR}/React-Fabric/React_Fabric.framework/Headers/react/renderer/components/view/platform/cxx","${PODS_CONFIGURATION_BUILD_DIR}/React-NativeModulesApple/React_NativeModulesApple.framework/Headers","${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers","${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios"],"LD_RUNPATH_SEARCH_PATHS":["/usr/lib/swift","$(inherited)"],"MTL_ENABLE_DEBUG_INFO":"NO","CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED":"YES","CLANG_WARN_NON_LITERAL_NULL_CONVERSION":"YES","VALIDATE_PRODUCT":"YES","GCC_WARN_UNUSED_FUNCTION":"YES","LIBRARY_SEARCH_PATHS":["\"$(SDKROOT)/usr/lib/swift\"","\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"","\"$(inherited)\""],"CLANG_WARN_OBJC_LITERAL_CONVERSION":"YES","IPHONEOS_DEPLOYMENT_TARGET":"15.1","CLANG_CXX_LIBRARY":"libc++"},"name":"Release"},"81AB9BB82411601600AC10FF":{"isa":"PBXBuildFile","fileRef":"81AB9BB72411601600AC10FF"},"13B07F861A680F5B00A75B9A":{"buildConfigurationList":"13B07F931A680F5B00A75B9A","productReference":"13B07F961A680F5B00A75B9A","productType":"com.apple.product-type.application","productName":"HelloWorld","isa":"PBXNativeTarget","buildPhases":["13B07F871A680F5B00A75B9A","13B07F8C1A680F5B00A75B9A","13B07F8E1A680F5B00A75B9A","00DD1BFF1BD5951E006B06BC"],"dependencies":[],"name":"HelloWorld","buildRules":[]},"00E356EB1AD99517003FC87E":{"isa":"PBXFrameworksBuildPhase","buildActionMask":"2147483647","files":[],"runOnlyForDeploymentPostprocessing":"0"},"00E356F71AD99517003FC87E":{"isa":"XCBuildConfiguration","buildSettings":{"IPHONEOS_DEPLOYMENT_TARGET":"15.1","COPY_PHASE_STRIP":"NO","LD_RUNPATH_SEARCH_PATHS":["$(inherited)","@executable_path/Frameworks","@loader_path/Frameworks"],"INFOPLIST_FILE":"HelloWorldTests/Info.plist","OTHER_LDFLAGS":["-ObjC","-lc++","$(inherited)"],"PRODUCT_BUNDLE_IDENTIFIER":"org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)","REACT_NATIVE_PATH":"${PROJECT_DIR}/../../../packages/react-native","BUNDLE_LOADER":"$(TEST_HOST)","PRODUCT_NAME":"$(TARGET_NAME)","TEST_HOST":"$(BUILT_PRODUCTS_DIR)/HelloWorld.app/HelloWorld"},"name":"Release"},"00E356F21AD99517003FC87E":{"path":"HelloWorldTests.m","isa":"PBXFileReference","lastKnownFileType":"sourcecode.c.objc","sourceTree":""},"13B07FB81A68108700A75B9A":{"path":"HelloWorld/PrivacyInfo.xcprivacy","isa":"PBXFileReference","lastKnownFileType":"text.plist.xml","name":"PrivacyInfo.xcprivacy","sourceTree":"","fileEncoding":"4"},"13B07F961A680F5B00A75B9A":{"path":"HelloWorld.app","isa":"PBXFileReference","includeInIndex":"0","explicitFileType":"wrapper.application","sourceTree":"BUILT_PRODUCTS_DIR"},"00E356EC1AD99517003FC87E":{"isa":"PBXResourcesBuildPhase","buildActionMask":"2147483647","files":[],"runOnlyForDeploymentPostprocessing":"0"},"13B07F871A680F5B00A75B9A":{"isa":"PBXSourcesBuildPhase","buildActionMask":"2147483647","files":["CDA0ED1A2D0B2D810079F561"],"runOnlyForDeploymentPostprocessing":"0"},"00DD1BFF1BD5951E006B06BC":{"inputPaths":["$(SRCROOT)/.xcode.env.local","$(SRCROOT)/.xcode.env","$(SRCROOT)/../.react-native.config"],"buildActionMask":"2147483647","shellPath":"/bin/sh","isa":"PBXShellScriptBuildPhase","runOnlyForDeploymentPostprocessing":"0","outputPaths":[],"shellScript":"set -e\n\nexport CONFIG_JSON=$(sed -e \"s|HELLOWORLD_PATH|$(realpath \"${SRCROOT}/../\")|g\" \"${SRCROOT}/../.react-native.config\")\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n","files":[],"name":"Bundle React Native code and images"},"BBD78D7AC51CEA395F1C20DB":{"path":"Pods","isa":"PBXGroup","children":[],"sourceTree":""},"0EC7AB76F90EED035707BA4E":{"path":"HelloWorld/PrivacyInfo.xcprivacy","isa":"PBXFileReference","includeInIndex":"1","name":"PrivacyInfo.xcprivacy","lastKnownFileType":"text.xml","sourceTree":""},"00E356F31AD99517003FC87E":{"isa":"PBXBuildFile","fileRef":"00E356F21AD99517003FC87E"},"2D16E6871FA4F8E400B85C8A":{"isa":"PBXGroup","name":"Frameworks","children":["ED297162215061F000B7C4FE"],"sourceTree":""},"00E356ED1AD99517003FC87E":{"buildConfigurationList":"00E357021AD99517003FC87E","productReference":"00E356EE1AD99517003FC87E","productType":"com.apple.product-type.bundle.unit-test","productName":"HelloWorldTests","isa":"PBXNativeTarget","buildPhases":["00E356EA1AD99517003FC87E","00E356EB1AD99517003FC87E","00E356EC1AD99517003FC87E"],"dependencies":["00E356F51AD99517003FC87E"],"name":"HelloWorldTests","buildRules":[]},"00E357021AD99517003FC87E":{"isa":"XCConfigurationList","defaultConfigurationIsVisible":"0","defaultConfigurationName":"Release","buildConfigurations":["00E356F61AD99517003FC87E","00E356F71AD99517003FC87E"]},"00E356F41AD99517003FC87E":{"isa":"PBXContainerItemProxy","containerPortal":"83CBB9F71A601CBA00E9B192","remoteGlobalIDString":"13B07F861A680F5B00A75B9A","remoteInfo":"HelloWorld","proxyType":"1"},"13B07FAE1A68108700A75B9A":{"isa":"PBXGroup","name":"HelloWorld","children":["13B07FB51A68108700A75B9A","13B07FB61A68108700A75B9A","81AB9BB72411601600AC10FF","13B07FB81A68108700A75B9A","0EC7AB76F90EED035707BA4E","CDA0ED192D0B2D810079F561"],"sourceTree":""},"13B07F8C1A680F5B00A75B9A":{"isa":"PBXFrameworksBuildPhase","buildActionMask":"2147483647","files":["1AA3D631FEB4A6CB2C936CB3","7FA267377AEF22168CBAE05E","DB6F0FEE98104AB555E0399D"],"runOnlyForDeploymentPostprocessing":"0"},"83CBBA001A601CBA00E9B192":{"isa":"PBXGroup","name":"Products","children":["13B07F961A680F5B00A75B9A","00E356EE1AD99517003FC87E"],"sourceTree":""},"13B07FB51A68108700A75B9A":{"path":"HelloWorld/Images.xcassets","isa":"PBXFileReference","name":"Images.xcassets","lastKnownFileType":"folder.assetcatalog","sourceTree":""},"00E356EE1AD99517003FC87E":{"path":"HelloWorldTests.xctest","isa":"PBXFileReference","includeInIndex":"0","explicitFileType":"wrapper.cfbundle","sourceTree":"BUILT_PRODUCTS_DIR"},"CDA0ED192D0B2D810079F561":{"path":"HelloWorld/AppDelegate.swift","isa":"PBXFileReference","name":"AppDelegate.swift","lastKnownFileType":"sourcecode.swift","sourceTree":""},"13B07F931A680F5B00A75B9A":{"isa":"XCConfigurationList","defaultConfigurationIsVisible":"0","defaultConfigurationName":"Release","buildConfigurations":["13B07F941A680F5B00A75B9A","13B07F951A680F5B00A75B9A"]},"83CBB9FA1A601CBA00E9B192":{"isa":"XCConfigurationList","defaultConfigurationIsVisible":"0","defaultConfigurationName":"Release","buildConfigurations":["83CBBA201A601CBA00E9B192","83CBBA211A601CBA00E9B192"]},"25D10417CDEB6879A4F00877":{"isa":"XCLocalSwiftPackageReference","relativePath":"../../../packages/react-native"},"B17015475BFCE5F8BBF1F304":{"isa":"XCSwiftPackageProductDependency","productName":"React"},"1AA3D631FEB4A6CB2C936CB3":{"isa":"PBXBuildFile","productRef":"B17015475BFCE5F8BBF1F304"},"EE1CB16CFB4619F31C5294CE":{"isa":"XCLocalSwiftPackageReference","relativePath":"build/generated/ios"},"F6EFC31EEA2F2E8014DAC0A6":{"isa":"XCSwiftPackageProductDependency","productName":"ReactCodegen"},"7FA267377AEF22168CBAE05E":{"isa":"PBXBuildFile","productRef":"F6EFC31EEA2F2E8014DAC0A6"},"5642A0789017A7E3132716AF":{"isa":"XCSwiftPackageProductDependency","productName":"ReactAppDependencyProvider"},"DB6F0FEE98104AB555E0399D":{"isa":"PBXBuildFile","productRef":"5642A0789017A7E3132716AF"}},"rootObject":"83CBB9F71A601CBA00E9B192"} \ No newline at end of file diff --git a/private/helloworld/ios/HelloWorld/Info.plist b/private/helloworld/ios/HelloWorld/Info.plist index 07b050898d9d..73938030310d 100644 --- a/private/helloworld/ios/HelloWorld/Info.plist +++ b/private/helloworld/ios/HelloWorld/Info.plist @@ -26,7 +26,6 @@ NSAppTransportSecurity - NSAllowsArbitraryLoads NSAllowsLocalNetworking @@ -34,6 +33,8 @@ NSLocationWhenInUseUsageDescription + RCTNewArchEnabled + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/private/helloworld/package.json b/private/helloworld/package.json index 757495928cac..2dad7d18e63d 100644 --- a/private/helloworld/package.json +++ b/private/helloworld/package.json @@ -9,7 +9,8 @@ "install-and-run": "node ./cli.js install-and-run", "launch": "node ./scripts/metro.js", "lint": "eslint .", - "test": "jest" + "test": "jest", + "prepare-with-swiftpm": "node ../../packages/react-native/scripts/swiftpm/prepare-app . ../../packages/react-native HelloWorld.xcodeproj HelloWorld" }, "dependencies": { "react": "19.1.1",