react-native 0.87.0-rc.1 → 0.87.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/Libraries/Core/InitializeCore.js +8 -1
  2. package/Libraries/Core/ReactNativeVersion.js +1 -1
  3. package/Libraries/ReactNative/AppRegistry.flow.js +1 -0
  4. package/Libraries/ReactNative/AppRegistryImpl.js +2 -3
  5. package/React/Base/RCTVersion.m +1 -1
  6. package/React/I18n/RCTLocalizedString.mm +38 -2
  7. package/React-Core-prebuilt.podspec +45 -17
  8. package/React-Core.podspec +9 -2
  9. package/ReactAndroid/external-artifacts/build.gradle.kts +49 -0
  10. package/ReactAndroid/gradle.properties +1 -1
  11. package/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/SynchronousMountItem.kt +8 -2
  12. package/ReactAndroid/src/main/java/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt +1 -1
  13. package/ReactCommon/cxxreact/ReactNativeVersion.h +1 -1
  14. package/package.json +10 -8
  15. package/react-native.config.js +81 -0
  16. package/scripts/cocoapods/fabric.rb +1 -1
  17. package/scripts/cocoapods/rncore.rb +35 -78
  18. package/scripts/cocoapods/rncore_facades.rb +232 -0
  19. package/scripts/cocoapods/rndependencies.rb +64 -3
  20. package/scripts/cocoapods/rndeps_facades.rb +193 -0
  21. package/scripts/cocoapods/spm.rb +78 -11
  22. package/scripts/codegen/templates/Package.swift.spm-template +97 -0
  23. package/scripts/react-native-xcode.sh +20 -0
  24. package/scripts/react_native_pods.rb +65 -16
  25. package/scripts/replace-rncore-version.js +53 -6
  26. package/scripts/setup-apple-spm.js +1165 -0
  27. package/scripts/spm/__doc__/rfc-spm-xcframework.md +707 -0
  28. package/scripts/spm/__doc__/spm-autolinking-plugins.md +244 -0
  29. package/scripts/spm/__doc__/spm-header-paths-contract.md +97 -0
  30. package/scripts/spm/__doc__/spm-plugins-assessment.md +128 -0
  31. package/scripts/spm/__doc__/spm-scripts.md +451 -0
  32. package/scripts/spm/autolinking-plugins.js +331 -0
  33. package/scripts/spm/download-spm-artifacts.js +1409 -0
  34. package/scripts/spm/expand-spm-dependencies.js +216 -0
  35. package/scripts/spm/flavored-frameworks.js +1008 -0
  36. package/scripts/spm/generate-spm-autolinking-config.js +161 -0
  37. package/scripts/spm/generate-spm-autolinking.js +1888 -0
  38. package/scripts/spm/generate-spm-package.js +302 -0
  39. package/scripts/spm/generate-spm-xcodeproj.js +2224 -0
  40. package/scripts/spm/read-podspec.js +695 -0
  41. package/scripts/spm/scaffold-package-swift.js +1206 -0
  42. package/scripts/spm/spm-pbxproj.js +654 -0
  43. package/scripts/spm/spm-types.js +517 -0
  44. package/scripts/spm/spm-utils.js +645 -0
  45. package/scripts/spm/sync-spm-autolinking.js +160 -0
  46. package/sdks/.hermesv1version +1 -0
  47. package/sdks/hermes-engine/utils/replace_hermes_version.js +18 -4
  48. package/sdks/hermes-engine/version.properties +1 -1
  49. package/third-party-podspecs/ReactNativeDependencies.podspec +2 -2
  50. package/types_generated/Libraries/ReactNative/AppRegistry.flow.d.ts +2 -2
  51. package/Libraries/Utilities/SceneTracker.js +0 -42
@@ -0,0 +1,193 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ #
3
+ # This source code is licensed under the MIT license found in the
4
+ # LICENSE file in the root directory of this source tree.
5
+
6
+ require 'json'
7
+ require 'fileutils'
8
+ # Self-contained against require ordering: this module reads
9
+ # Helpers::Constants.socket_rocket_config. react_native_pods.rb normally loads
10
+ # helpers.rb first, but requiring it here (idempotent) removes that implicit
11
+ # dependency. The defined? guard at the use site stays as a backstop.
12
+ require_relative './helpers'
13
+
14
+ # Dependency-only facade podspecs for the third-party deps in prebuilt-deps
15
+ # mode (deps-side analogue of RNCoreFacades). Design + rationale:
16
+ # scripts/cocoapods/__docs__/prebuilt-deps.md
17
+ module RNDepsFacades
18
+ # The name of the umbrella prebuilt-deps pod every facade depends on. Its pod
19
+ # self-serves the third-party headers + carries the binary (see
20
+ # rndependencies.rb, ReactNativeDependencies.podspec).
21
+ DEPS_POD = "ReactNativeDependencies"
22
+
23
+ # pod name => podspec path (relative to the react-native package root), or
24
+ # :synthesized for a pod with no local podspec (SocketRocket). Version +
25
+ # subspecs + default_subspecs are DERIVED from the real podspec where one
26
+ # exists; synthesized entries derive their version from a constant.
27
+ FACADE_PODS = {
28
+ "RCT-Folly" => "third-party-podspecs/RCT-Folly.podspec",
29
+ "glog" => "third-party-podspecs/glog.podspec",
30
+ "boost" => "third-party-podspecs/boost.podspec",
31
+ "DoubleConversion" => "third-party-podspecs/DoubleConversion.podspec",
32
+ "fmt" => "third-party-podspecs/fmt.podspec",
33
+ "fast_float" => "third-party-podspecs/fast_float.podspec",
34
+ "SocketRocket" => :synthesized,
35
+ }
36
+
37
+ # Sub-directory (relative to the install root) that holds the generated
38
+ # deps facades. Kept separate from RNCoreFacades' `build/rncore-facades` so
39
+ # the two families never collide.
40
+ FACADE_RELDIR = File.join("build", "rndeps-facades")
41
+
42
+ # Generates the facade podspecs and returns the base directory holding them.
43
+ # Each facade gets its OWN sub-directory containing a single
44
+ # `<Name>.podspec.json`, so it can be installed as a LOCAL pod via
45
+ # `:path => <dir>` (PathSource uses the spec in place and never downloads
46
+ # `spec.source`). Idempotent; safe to call once per `pod install`.
47
+ #
48
+ # `react_native_path` locates the real third-party podspecs we mirror.
49
+ # version + subspecs + default_subspecs are DERIVED from the real spec (or,
50
+ # for SocketRocket, synthesized from the socket_rocket_config version) so the
51
+ # facade matches the source pod's spec/subspec SHAPE. It is not fully
52
+ # graph-equivalent: every derived subspec depends only on
53
+ # ReactNativeDependencies, so intra-pod subspec deps (e.g. RCT-Folly/Fabric
54
+ # -> RCT-Folly/Default) are not reproduced — harmless here because the deps
55
+ # are all declared explicitly in react_native_pods.rb. NO source_files and
56
+ # NO headers are emitted — the ReactNativeDependencies pod supplies both. A
57
+ # facaded pod whose real podspec can't be read is a hard error (see
58
+ # load_real_spec) — silently shipping an empty facade would hide drift.
59
+ def self.generate(react_native_path, install_root, ios_version)
60
+ abs_base = File.join(install_root.to_s, FACADE_RELDIR)
61
+ FileUtils.mkdir_p(abs_base)
62
+ FACADE_PODS.each do |name, podspec_rel_path|
63
+ dir = File.join(abs_base, name)
64
+ FileUtils.mkdir_p(dir)
65
+
66
+ if podspec_rel_path == :synthesized
67
+ spec = synthesized_spec(name, ios_version)
68
+ else
69
+ podspec_path = File.join(react_native_path.to_s, podspec_rel_path)
70
+ real = load_real_spec(podspec_path, name)
71
+ spec = derived_spec(name, real, ios_version)
72
+ end
73
+
74
+ File.write(File.join(dir, "#{name}.podspec.json"), JSON.pretty_generate(spec))
75
+ end
76
+ abs_base
77
+ end
78
+
79
+ # Facade dir for `<name>`, RELATIVE to the install root — pass to `pod :path =>`.
80
+ # Relative (not absolute) so the path CocoaPods records in Podfile.lock is
81
+ # portable rather than machine-specific.
82
+ def self.facade_path(name)
83
+ File.join(FACADE_RELDIR, name)
84
+ end
85
+
86
+ # Base spec skeleton shared by derived + synthesized facades: dependency-only,
87
+ # no source_files, no headers. Depends solely on ReactNativeDependencies.
88
+ def self.base_spec(name, version, ios_version)
89
+ {
90
+ "name" => name,
91
+ "version" => version,
92
+ "summary" => "Prebuilt facade for #{name} (code + headers live in #{DEPS_POD}).",
93
+ "homepage" => "https://reactnative.dev/",
94
+ "license" => "MIT",
95
+ "authors" => "Meta Platforms, Inc. and its affiliates",
96
+ "platforms" => { "ios" => ios_version },
97
+ # Required podspec attribute, but never fetched: installed as a LOCAL
98
+ # pod (`:path => <dir>`), which uses this spec in place and ships no
99
+ # source_files. Placeholder only.
100
+ "source" => { "git" => "https://github.com/facebook/react-native.git" },
101
+ "dependencies" => { DEPS_POD => [] },
102
+ }
103
+ end
104
+ private_class_method :base_spec
105
+
106
+ # Facade derived from a real third-party podspec: version + subspecs +
107
+ # default_subspecs mirror the real spec so a bare `pod '<Name>'` and any
108
+ # `pod '<Name>/<Subspec>'` resolve to the SAME graph (e.g. RCT-Folly's
109
+ # bare + /Default + /Fabric). Each subspec is also dependency-only and
110
+ # depends on ReactNativeDependencies.
111
+ #
112
+ # NOTE: resources (e.g. RCT-Folly's PrivacyInfo.xcprivacy) are intentionally
113
+ # NOT carried. In prebuilt-deps mode the third-party code — and its privacy
114
+ # manifest — is embedded in the ReactNativeDependencies artifact; the facade
115
+ # only needs to declare the dependency (see the design note in the PR).
116
+ def self.derived_spec(name, real, ios_version)
117
+ spec = base_spec(name, real.version.to_s, ios_version)
118
+
119
+ defaults = Array(real.default_subspecs)
120
+ spec["default_subspecs"] = defaults unless defaults.empty?
121
+
122
+ subspecs = derive_subspecs(real)
123
+ unless subspecs.empty?
124
+ spec["subspecs"] = subspecs.map do |ss|
125
+ { "name" => ss, "dependencies" => { DEPS_POD => [] } }
126
+ end
127
+ end
128
+
129
+ spec
130
+ end
131
+ private_class_method :derived_spec
132
+
133
+ # Facade synthesized for a pod with NO local podspec (SocketRocket, a trunk
134
+ # pod). Version comes from Helpers::Constants::socket_rocket_config; no
135
+ # subspecs. A missing/blank constant is a hard error rather than a silent
136
+ # versionless facade — a bare `pod 'SocketRocket'` in the source path is
137
+ # `"~> #{socket_rocket_config[:version]}"`, so the facade MUST carry a version
138
+ # that satisfies that constraint.
139
+ def self.synthesized_spec(name, ios_version)
140
+ version = synthesized_version(name)
141
+ base_spec(name, version, ios_version)
142
+ end
143
+ private_class_method :synthesized_spec
144
+
145
+ # Resolves the synthesized version for a no-podspec facade. Fail-closed on a
146
+ # missing constant/version. Only SocketRocket is synthesized today.
147
+ def self.synthesized_version(name)
148
+ case name
149
+ when "SocketRocket"
150
+ unless defined?(Helpers::Constants) && Helpers::Constants.respond_to?(:socket_rocket_config)
151
+ raise "[RNDepsFacades] Cannot synthesize facade for '#{name}': " \
152
+ "Helpers::Constants.socket_rocket_config is unavailable."
153
+ end
154
+ version = Helpers::Constants.socket_rocket_config[:version]
155
+ if version.nil? || version.to_s.strip.empty?
156
+ raise "[RNDepsFacades] Cannot synthesize facade for '#{name}': " \
157
+ "socket_rocket_config[:version] is missing or empty."
158
+ end
159
+ version.to_s
160
+ else
161
+ raise "[RNDepsFacades] No synthesized version rule for facaded pod '#{name}'. " \
162
+ "Add one to synthesized_version or give it a real podspec in FACADE_PODS."
163
+ end
164
+ end
165
+ private_class_method :synthesized_version
166
+
167
+ # Loads the real podspec so we can mirror its structure. A facaded pod with a
168
+ # declared podspec path MUST have a readable real podspec — if it's missing or
169
+ # unparseable we raise rather than ship an empty facade (which would silently
170
+ # drop subspecs / the version, the very drift this mechanism prevents).
171
+ def self.load_real_spec(path, name)
172
+ unless File.exist?(path)
173
+ raise "[RNDepsFacades] Real podspec for facaded pod '#{name}' not found at #{path}. " \
174
+ "Update FACADE_PODS in rndeps_facades.rb if the podspec moved."
175
+ end
176
+ begin
177
+ Pod::Specification.from_file(path)
178
+ rescue => e
179
+ raise "[RNDepsFacades] Failed to read real podspec for facaded pod '#{name}' at #{path}: #{e.message}"
180
+ end
181
+ end
182
+ private_class_method :load_real_spec
183
+
184
+ # Library (non-test, non-app) subspec names of the real spec, so third-party
185
+ # libs depending on `<pod>/<subspec>` (e.g. `RCT-Folly/Fabric`) keep
186
+ # resolving. Derived, never hand-listed.
187
+ def self.derive_subspecs(real)
188
+ real.subspecs
189
+ .reject { |ss| ss.test_specification? || (ss.respond_to?(:app_specification?) && ss.app_specification?) }
190
+ .map(&:base_name)
191
+ end
192
+ private_class_method :derive_subspecs
193
+ end
@@ -19,28 +19,46 @@ class SPMManager
19
19
  log 'Cleaning old SPM dependencies from Pods project'
20
20
  clean_spm_dependencies_from_target(project, @dependencies_by_pod)
21
21
  log 'Adding SPM dependencies to Pods project'
22
+ flattened_pod_names = []
22
23
  @dependencies_by_pod.each do |pod_name, dependencies|
24
+ target = project.targets.find { |t| t.name == pod_name}
23
25
  dependencies.each do |spm_spec|
24
26
  log "Adding SPM dependency on product #{spm_spec[:products]}"
25
27
  add_spm_to_target(
26
28
  project,
27
- project.targets.find { |t| t.name == pod_name},
29
+ target,
28
30
  spm_spec[:url],
29
31
  spm_spec[:requirement],
30
32
  spm_spec[:products]
31
33
  )
32
- log " Adding workaround for Swift package not found issue"
33
- target = project.targets.find { |t| t.name == pod_name}
34
- target.build_configurations.each do |config|
35
- target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'] ||= ['$(inherited)']
36
- search_path = '${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/'
37
- unless target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].include?(search_path)
38
- target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].push(search_path)
34
+ if target.product_type == 'com.apple.product-type.library.static'
35
+ # Xcode emits a package C-target's module map both into the shared
36
+ # products dir and into this pod's CONFIGURATION_BUILD_DIR, and both
37
+ # copies are unavoidably visible (the package swiftmodule serializes
38
+ # the products-root path) — clang hard-errors with "redefinition of
39
+ # module" on Xcode 26.3. Building the pod straight into the products
40
+ # root makes both paths the same file, and puts the package
41
+ # swiftmodule on the pod's default search path.
42
+ log " Building #{pod_name} into the shared products dir to avoid duplicate module maps"
43
+ target.build_configurations.each do |config|
44
+ target.build_settings(config.name)['CONFIGURATION_BUILD_DIR'] = '${PODS_CONFIGURATION_BUILD_DIR}'
45
+ end
46
+ flattened_pod_names << pod_name unless flattened_pod_names.include?(pod_name)
47
+ else
48
+ log " Adding workaround for Swift package not found issue"
49
+ target.build_configurations.each do |config|
50
+ target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'] ||= ['$(inherited)']
51
+ search_path = '${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/'
52
+ unless target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].include?(search_path)
53
+ target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].push(search_path)
54
+ end
39
55
  end
40
56
  end
41
57
  end
42
58
  end
43
59
 
60
+ rewrite_aggregate_modulemap_references(installer, flattened_pod_names) unless flattened_pod_names.empty?
61
+
44
62
  unless @dependencies_by_pod.empty?
45
63
  log_warning "If you're using Xcode 15 or earlier you might need to close and reopen the Xcode workspace"
46
64
  unless ENV["USE_FRAMEWORKS"] == "dynamic"
@@ -53,6 +71,55 @@ class SPMManager
53
71
 
54
72
  private
55
73
 
74
+ # Flattening a pod's build dir moves its generated modulemap from
75
+ # "<Pod>/<Pod>.modulemap" to "<Pod>.modulemap"; the aggregate xcconfigs
76
+ # reference the old path via -fmodule-map-file. Must mutate the in-memory
77
+ # Config attributes — later post_install steps (NewArchitectureHelper) re-save
78
+ # the same Config objects and would clobber a plain file edit.
79
+ def rewrite_aggregate_modulemap_references(installer, pod_names)
80
+ installer.aggregate_targets.each do |aggregate_target|
81
+ aggregate_target.xcconfigs.each do |config_name, config_file|
82
+ %w[OTHER_CFLAGS OTHER_SWIFT_FLAGS].each do |key|
83
+ value = config_file.attributes[key]
84
+ next unless value
85
+
86
+ updated_value = pod_names.reduce(value) do |acc, pod_name|
87
+ acc.gsub(
88
+ "${PODS_CONFIGURATION_BUILD_DIR}/#{pod_name}/#{pod_name}.modulemap",
89
+ "${PODS_CONFIGURATION_BUILD_DIR}/#{pod_name}.modulemap"
90
+ )
91
+ end
92
+ config_file.attributes[key] = updated_value if updated_value != value
93
+ end
94
+
95
+ config_file.save_as(aggregate_target.xcconfig_path(config_name))
96
+ end
97
+ end
98
+ end
99
+
100
+ # Creates a new object in the project with a UUID guaranteed not to collide
101
+ # with any UUID already present in the project.
102
+ #
103
+ # `Pod::Project` overrides `generate_available_uuid_list` with a fast,
104
+ # counter-based scheme (`<sha prefix><counter>0`) that deliberately skips
105
+ # collision checks, on the assumption that the whole Pods project is generated
106
+ # in a single pass. That assumption does not hold here: we run in a
107
+ # `post_install` hook, and the generator's counter can be out of sync with the
108
+ # UUIDs already assigned to existing objects (e.g. when the project has been
109
+ # reloaded from disk during an incremental install, the counter restarts at 0
110
+ # while the root object still occupies `<prefix>00000000`). Using `project.new`
111
+ # directly can therefore hand back a UUID that is already in use and overwrite
112
+ # an existing object (notably the root `PBXProject`), producing a Pods project
113
+ # Xcode refuses to load. We keep the deterministic scheme but probe forward
114
+ # until we find a UUID that is actually free.
115
+ def new_object(project, klass)
116
+ uuid = project.generate_uuid
117
+ uuid = project.generate_uuid while project.objects_by_uuid.key?(uuid)
118
+ object = klass.new(project, uuid)
119
+ object.initialize_defaults
120
+ object
121
+ end
122
+
56
123
  def log(msg)
57
124
  ::Pod::UI.puts "[SPM] #{msg}"
58
125
  end
@@ -76,7 +143,7 @@ class SPMManager
76
143
  pkg_class = Xcodeproj::Project::Object::XCLocalSwiftPackageReference
77
144
  pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.relative_path == url }
78
145
  if !pkg
79
- pkg = project.new(pkg_class)
146
+ pkg = new_object(project, pkg_class)
80
147
  pkg.relative_path = url
81
148
  log(" Adding local package to workspace: #{pkg.inspect}")
82
149
  project.root_object.package_references << pkg
@@ -85,7 +152,7 @@ class SPMManager
85
152
  pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference
86
153
  pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.repositoryURL == url }
87
154
  if !pkg
88
- pkg = project.new(pkg_class)
155
+ pkg = new_object(project, pkg_class)
89
156
  pkg.repositoryURL = url
90
157
  pkg.requirement = requirement
91
158
  log(" Adding remote package to workspace: #{pkg.inspect}")
@@ -101,7 +168,7 @@ class SPMManager
101
168
  next if ref
102
169
 
103
170
  log(" Adding product dependency #{product_name} to #{target.name}")
104
- ref = project.new(ref_class)
171
+ ref = new_object(project, ref_class)
105
172
  ref.package = pkg
106
173
  ref.product_name = product_name
107
174
  target.package_product_dependencies << ref
@@ -0,0 +1,97 @@
1
+ // swift-tools-version: 6.0
2
+ /*
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
9
+ // AUTO-GENERATED by scripts/setup-apple-spm.js – do not edit manually.
10
+ // This SPM-specific template replaces the CocoaPods codegen template
11
+ // with xcframework configuration built in.
12
+
13
+ import PackageDescription
14
+
15
+ // React headers need NO search paths: the React module comes from the
16
+ // invariant ReactHeaders Clang target,
17
+ // every other RN namespace from the ReactNativeHeaders binaryTarget
18
+ // (pure-RN), the third-party deps namespaces (folly/glog/boost/...) from the
19
+ // ReactNativeDependenciesHeaders sidecar, and the per-app generated headers
20
+ // from the ReactAppHeaders target below — all auto-served by SPM through
21
+ // product/target dependencies.
22
+ let headersDep: [Target.Dependency] = [
23
+ .product(name: "ReactHeaders", package: "ReactNative"),
24
+ .product(name: "ReactNativeHeaders", package: "ReactNative"),
25
+ .product(name: "ReactNativeDependenciesHeaders", package: "ReactNative"),
26
+ "ReactAppHeaders",
27
+ ]
28
+
29
+ let package = Package(
30
+ name: "React-GeneratedCode",
31
+ platforms: [.iOS(.v15), .macCatalyst(SupportedPlatform.MacCatalystVersion.v13)],
32
+ products: [
33
+ .library(
34
+ name: "ReactCodegen",
35
+ targets: ["ReactCodegen"]),
36
+ .library(
37
+ name: "ReactAppDependencyProvider",
38
+ targets: ["ReactAppDependencyProvider"]),
39
+ .library(
40
+ name: "ReactAppHeaders",
41
+ targets: ["ReactAppHeaders"]),
42
+ ],
43
+ dependencies: [
44
+ .package(name: "ReactNative", path: "../../xcframeworks"),
45
+ ],
46
+ targets: [
47
+ .target(
48
+ name: "ReactCodegen",
49
+ dependencies: headersDep,
50
+ path: "ReactCodegen",
51
+ exclude: ["ReactCodegen.podspec"],
52
+ publicHeadersPath: ".",
53
+ cSettings: [.headerSearchPath("headers")],
54
+ // DEBUG/NDEBUG match the prebuilt React.framework's config-gated C++
55
+ // ABI: NDEBUG (Release) strips DebugStringConvertible's vtable and
56
+ // shifts the ShadowNode layout that codegen'd Props inherit, so this
57
+ // target must compile with the same NDEBUG state or the Release link
58
+ // fails (undefined "vtable for DebugStringConvertible"). Mirrors
59
+ // packages/react-native/Package.swift.
60
+ cxxSettings: [
61
+ .headerSearchPath("headers"),
62
+ .define("DEBUG", .when(configuration: .debug)),
63
+ .define("NDEBUG", .when(configuration: .release)),
64
+ ],
65
+ linkerSettings: [
66
+ .linkedFramework("Foundation")
67
+ ]
68
+ ),
69
+ .target(
70
+ name: "ReactAppDependencyProvider",
71
+ dependencies: ["ReactCodegen"] + headersDep,
72
+ path: "ReactAppDependencyProvider",
73
+ exclude: ["ReactAppDependencyProvider.podspec"],
74
+ publicHeadersPath: ".",
75
+ cSettings: [.headerSearchPath(".."), .headerSearchPath("headers")],
76
+ cxxSettings: [
77
+ .headerSearchPath(".."),
78
+ .headerSearchPath("headers"),
79
+ // Match the prebuilt React.framework NDEBUG-gated C++ ABI (see above).
80
+ .define("DEBUG", .when(configuration: .debug)),
81
+ .define("NDEBUG", .when(configuration: .release)),
82
+ ],
83
+ linkerSettings: [
84
+ .linkedFramework("Foundation")
85
+ ]
86
+ ),
87
+ // The per-app generated-headers farm (built by spm sync), vended as a
88
+ // normal SPM headers target.
89
+ .target(
90
+ name: "ReactAppHeaders",
91
+ path: "ReactAppHeaders",
92
+ publicHeadersPath: "."
93
+ ),
94
+ ],
95
+ // React Native headers require C++20 (concepts, std::optional, etc.)
96
+ cxxLanguageStandard: .cxx20
97
+ )
@@ -80,6 +80,26 @@ source "$REACT_NATIVE_DIR/scripts/node-binary.sh"
80
80
  HERMES_ENGINE_PATH="$PODS_ROOT/hermes-engine"
81
81
  [ -z "$HERMES_CLI_PATH" ] && HERMES_CLI_PATH="$HERMES_ENGINE_PATH/destroot/bin/hermesc"
82
82
 
83
+ # SwiftPM consumers have no Pods directory to provide hermesc. When there is
84
+ # no Pods installation at all and the current HERMES_CLI_PATH does not exist
85
+ # (unset, or an injector-baked path gone stale after a package-store move),
86
+ # resolve the hermes-compiler npm package THROUGH react-native's own dependency
87
+ # graph — react-native pins the hermes-compiler version, so the compiler's
88
+ # bytecode version always matches the prebuilt hermes VM artifacts (a
89
+ # mismatched pair crashes at launch with "Wrong bytecode version").
90
+ # STRICTLY SwiftPM-scoped: in a CocoaPods build the hermes-engine pod owns both
91
+ # the VM and hermesc, and its hermes train may legitimately differ from the npm
92
+ # hermes-compiler — falling back there would compile bundles the podded VM
93
+ # rejects.
94
+ if [[ ( -z "$PODS_ROOT" || ! -d "$PODS_ROOT" ) && ! -f "$HERMES_CLI_PATH" ]]; then
95
+ NODE_HERMESC=$(RN_DIR="$REACT_NATIVE_DIR" "$NODE_BINARY" --print \
96
+ "require('path').join(require('path').dirname(require.resolve('hermes-compiler/package.json', {paths: [process.env.RN_DIR]})), 'hermesc', 'osx-bin', 'hermesc')" \
97
+ 2>/dev/null || true)
98
+ if [[ -n "$NODE_HERMESC" && -f "$NODE_HERMESC" ]]; then
99
+ HERMES_CLI_PATH="$NODE_HERMESC"
100
+ fi
101
+ fi
102
+
83
103
  # If hermesc is not available and USE_HERMES is not set to false, show error.
84
104
  if [[ $USE_HERMES != false && -f "$HERMES_ENGINE_PATH" && ! -f "$HERMES_CLI_PATH" ]]; then
85
105
  echo "error: Hermes is enabled but the hermesc binary could not be found at ${HERMES_CLI_PATH}." \
@@ -21,6 +21,8 @@ require_relative './cocoapods/helpers.rb'
21
21
  require_relative './cocoapods/privacy_manifest_utils.rb'
22
22
  require_relative './cocoapods/spm.rb'
23
23
  require_relative './cocoapods/rncore.rb'
24
+ require_relative './cocoapods/rncore_facades.rb'
25
+ require_relative './cocoapods/rndeps_facades.rb'
24
26
  # Importing to expose use_native_modules!
25
27
  require_relative './cocoapods/autolinking.rb'
26
28
 
@@ -52,6 +54,28 @@ def prepare_react_native_project!
52
54
  ReactNativePodsUtils.create_xcode_env_if_missing
53
55
  end
54
56
 
57
+ # Declares a React core pod, choosing source vs prebuilt facade. In prebuilt
58
+ # mode, pods in the RNCoreFacades manifest are installed as dependency-only
59
+ # facades (no source/headers) so they can't shadow the prebuilt artifact; their
60
+ # code + headers come from React-Core-prebuilt. Everything else (and the whole
61
+ # source build) is unaffected. See cocoapods/rncore_facades.rb.
62
+ def rncore_pod(name, **opts)
63
+ base = name.split('/').first
64
+ if !ReactNativeCoreUtils.build_rncore_from_source() && RNCoreFacades.facade?(base)
65
+ # Install as a LOCAL pod (`:path`) from the generated facade directory, so
66
+ # CocoaPods never fetches the placeholder git source (a `:podspec` external
67
+ # source would). Both the pod and any subspec declaration point at the SAME
68
+ # directory, so CocoaPods sees one consistent source for the name (a bare
69
+ # subspec declaration would otherwise default to the spec repo and conflict).
70
+ # Preserve the caller's options (e.g. :modular_headers) but replace :path with
71
+ # the facade directory.
72
+ facade_opts = opts.reject { |k, _| k == :path }
73
+ pod name, **facade_opts, :path => RNCoreFacades.facade_path(base)
74
+ else
75
+ pod name, **opts
76
+ end
77
+ end
78
+
55
79
  # Function that setup all the react native dependencies
56
80
 
57
81
  # Parameters
@@ -123,19 +147,25 @@ def use_react_native! (
123
147
  # Update ReactNativeCoreUtils so that we can easily switch between source and prebuilt
124
148
  ReactNativeCoreUtils.setup_rncore(prefix, react_native_version)
125
149
 
150
+ # In prebuilt mode, generate the facade podspecs the core pods are installed as
151
+ # (instead of their source podspecs) so they don't ship shadowing headers.
152
+ unless ReactNativeCoreUtils.build_rncore_from_source()
153
+ RNCoreFacades.generate(react_native_path, Pod::Config.instance.installation_root, react_native_version, min_ios_version_supported)
154
+ end
155
+
126
156
  Pod::UI.puts "Configuring the target with the New Architecture\n"
127
157
 
128
158
  # The Pods which should be included in all projects
129
- pod 'FBLazyVector', :path => "#{prefix}/Libraries/FBLazyVector"
130
- pod 'RCTRequired', :path => "#{prefix}/Libraries/Required"
159
+ rncore_pod 'FBLazyVector', :path => "#{prefix}/Libraries/FBLazyVector"
160
+ rncore_pod 'RCTRequired', :path => "#{prefix}/Libraries/Required"
131
161
  pod 'RCTTypeSafety', :path => "#{prefix}/Libraries/TypeSafety", :modular_headers => true
132
162
  pod 'React', :path => "#{prefix}/"
133
163
  if !ReactNativeCoreUtils.build_rncore_from_source()
134
164
  pod 'React-Core-prebuilt', :podspec => "#{prefix}/React-Core-prebuilt.podspec", :modular_headers => true
135
165
  end
136
- pod 'React-Core', :path => "#{prefix}/"
166
+ rncore_pod 'React-Core', :path => "#{prefix}/"
137
167
  pod 'React-CoreModules', :path => "#{prefix}/React/CoreModules"
138
- pod 'React-RCTRuntime', :path => "#{prefix}/React/Runtime"
168
+ rncore_pod 'React-RCTRuntime', :path => "#{prefix}/React/Runtime"
139
169
  pod 'React-RCTAppDelegate', :path => "#{prefix}/Libraries/AppDelegate"
140
170
  pod 'React-RCTActionSheet', :path => "#{prefix}/Libraries/ActionSheetIOS"
141
171
  pod 'React-RCTAnimation', :path => "#{prefix}/Libraries/NativeAnimation"
@@ -146,7 +176,7 @@ def use_react_native! (
146
176
  pod 'React-RCTSettings', :path => "#{prefix}/Libraries/Settings"
147
177
  pod 'React-RCTText', :path => "#{prefix}/Libraries/Text"
148
178
  pod 'React-RCTVibration', :path => "#{prefix}/Libraries/Vibration"
149
- pod 'React-Core/RCTWebSocket', :path => "#{prefix}/"
179
+ rncore_pod 'React-Core/RCTWebSocket', :path => "#{prefix}/"
150
180
  pod 'React-cxxreact', :path => "#{prefix}/ReactCommon/cxxreact"
151
181
  pod 'React-cxxstableapi', :path => "#{prefix}/ReactCommon/react/cxxstableapi"
152
182
  pod 'React-debug', :path => "#{prefix}/ReactCommon/react/debug"
@@ -164,7 +194,7 @@ def use_react_native! (
164
194
  pod 'React-Mapbuffer', :path => "#{prefix}/ReactCommon"
165
195
  pod 'React-bridging', :path => "#{prefix}/ReactCommon/react/bridging", :modular_headers => true
166
196
  pod 'React-jserrorhandler', :path => "#{prefix}/ReactCommon/jserrorhandler"
167
- pod 'RCTDeprecation', :path => "#{prefix}/ReactApple/Libraries/RCTFoundation/RCTDeprecation"
197
+ rncore_pod 'RCTDeprecation', :path => "#{prefix}/ReactApple/Libraries/RCTFoundation/RCTDeprecation"
168
198
  pod 'React-RCTFBReactNativeSpec', :path => "#{prefix}/React"
169
199
  pod 'React-jsi', :path => "#{prefix}/ReactCommon/jsi"
170
200
  pod 'RCTSwiftUI', :path => "#{prefix}/ReactApple/RCTSwiftUI"
@@ -197,7 +227,7 @@ def use_react_native! (
197
227
  pod 'React-logger', :path => "#{prefix}/ReactCommon/logger"
198
228
  pod 'ReactCommon/turbomodule/core', :path => "#{prefix}/ReactCommon", :modular_headers => true
199
229
  pod 'React-NativeModulesApple', :path => "#{prefix}/ReactCommon/react/nativemodule/core/platform/ios", :modular_headers => true
200
- pod 'Yoga', :path => "#{prefix}/ReactCommon/yoga", :modular_headers => true
230
+ rncore_pod 'Yoga', :path => "#{prefix}/ReactCommon/yoga", :modular_headers => true
201
231
  setup_fabric!(:react_native_path => prefix)
202
232
  setup_bridgeless!(:react_native_path => prefix, :use_hermes => hermes_enabled)
203
233
 
@@ -214,6 +244,17 @@ def use_react_native! (
214
244
  ReactNativeCoreUtils.rncore_log("Using React Native Core and React Native Dependencies prebuilt versions.")
215
245
  pod 'ReactNativeDependencies', :podspec => "#{prefix}/third-party-podspecs/ReactNativeDependencies.podspec", :modular_headers => true
216
246
 
247
+ # Facades: community pods' hardcoded s.dependency "RCT-Folly"/"glog"/... must
248
+ # resolve locally instead of from trunk. See __docs__/prebuilt-deps.md.
249
+ RNDepsFacades.generate(react_native_path, Pod::Config.instance.installation_root, min_ios_version_supported)
250
+ pod 'DoubleConversion', :path => RNDepsFacades.facade_path('DoubleConversion')
251
+ pod 'glog', :path => RNDepsFacades.facade_path('glog')
252
+ pod 'boost', :path => RNDepsFacades.facade_path('boost')
253
+ pod 'fast_float', :path => RNDepsFacades.facade_path('fast_float')
254
+ pod 'fmt', :path => RNDepsFacades.facade_path('fmt')
255
+ pod 'RCT-Folly', :path => RNDepsFacades.facade_path('RCT-Folly')
256
+ pod 'SocketRocket', :path => RNDepsFacades.facade_path('SocketRocket')
257
+
217
258
  if !ReactNativeCoreUtils.build_rncore_from_source()
218
259
  pod 'React-Core-prebuilt', :podspec => "#{prefix}/React-Core-prebuilt.podspec", :modular_headers => true
219
260
  end
@@ -569,18 +610,26 @@ def react_native_post_install(
569
610
  ReactNativePodsUtils.add_ndebug_flag_to_pods_in_release(installer)
570
611
 
571
612
  if !ReactNativeCoreUtils.build_rncore_from_source()
572
- # In XCode 26 we need to revert the new setting SWIFT_ENABLE_EXPLICIT_MODULES when building
573
- # with precompiled binaries.
574
- ReactNativePodsUtils.set_build_setting(installer, build_setting: "SWIFT_ENABLE_EXPLICIT_MODULES", value: "NO")
575
-
576
- # Process the VFS overlay for prebuilt React Native Core - this is done as part of the post install so
577
- # that we can update paths based on the final location of the Pods installation.
578
- ReactNativeCoreUtils.process_vfs_overlay()
579
-
580
- # Configure xcconfig for prebuilt usage (VFS overlay, header paths, cleanup redundant paths)
613
+ # The Xcode-26 SWIFT_ENABLE_EXPLICIT_MODULES=NO workaround (#53457) is removed:
614
+ # the modular ReactNativeHeaders layout + React-Core-prebuilt module-map
615
+ # activation + header-less facades let the React module precompile cleanly with
616
+ # explicit modules ON (verified cold-DD green), so the override is unnecessary.
617
+
618
+ # Make the prebuilt React.xcframework headers resolvable from aggregate (main app)
619
+ # and third-party pod targets that don't go through add_rncore_dependency. The headers
620
+ # are served directly from the xcframework's headers-spec layout — no clang VFS overlay.
581
621
  ReactNativeCoreUtils.configure_aggregate_xcconfig(installer)
582
622
  end
583
623
 
624
+ if !ReactNativeDependenciesUtils.build_react_native_deps_from_source()
625
+ # Prebuilt-deps mode: make the deps artifact headers (folly/glog/...)
626
+ # resolvable from the aggregate and every pod target, mirroring the rncore
627
+ # injection above. ReactNativeHeaders is pure-RN, so the flattened
628
+ # ReactNativeDependencies/Headers dir is the single global home of the
629
+ # third-party namespaces (see scripts/cocoapods/__docs__/prebuilt-deps.md).
630
+ ReactNativeDependenciesUtils.configure_aggregate_xcconfig(installer)
631
+ end
632
+
584
633
  SPM.apply_on_post_install(installer)
585
634
 
586
635
  if privacy_file_aggregation_enabled