refactor(iOS): include resources like bundles into the precompiled XCFrameworks - #57305
refactor(iOS): include resources like bundles into the precompiled XCFrameworks#57305chrfalch wants to merge 28 commits into
Conversation
9b0b73d to
2c1bf1d
Compare
c2c6397 to
2540bdc
Compare
2540bdc to
72768b5
Compare
6255670 to
8158f41
Compare
…S overlay ## Summary In RN 0.87 we'll hopefully release a new version of our precompiled binaries that works without the VFS overlay: - react/react-native#57285 - removal of VFS overlay - react/react-native#57305 - moved resource bundles to xcframework Meaning that we'll get completely self-contained XCFrameworks from RN which is a prerequisite for SwiftPM to not become too complex. ## How This PR addds support for detecting wether we're running with an RN version that uses these new frameworks or not - making sure we fallback to the old version when running with pre 0.87. In addition it updates the pod install pipeline with the same tests. NOTE: When merging 0.87 into Expo (as our current React Native), we can remove a lot of code in a follow-up PR that removes the VFS support all-over. There are a couple of 3rd party packages not compatible with RN 0.87 yet, so I tested with some patches for these packages. In addition a small binary fix was added to fix a missing method in 0.87 in EXpo's code. ## Test-plan - ✅ Built Bare-Expo, minimal-tester and precompiled with the new binaries from React native - ✅ Verified that the same versions are using VFS and precompiling correctly when using RN 0.86
The minimal machinery to build the packaged header structures: - headers-spec.js: the executable layout contract (rules R1-R8) — which namespaces are hoisted into the React framework, which carry module maps, and how collisions are rejected. - headers-inventory.js: scans the source tree and classifies every shipped header (language surface + modularizability bucket) — the input to the spec. computeInventory() feeds the build in-memory; the CLI writes a JSON manifest. - headers-compose.js: emits the layout — writes the <React/...> headers + umbrella + module map into each React.framework slice (detected by the framework's presence), and assembles the headers-only ReactNativeHeaders.xcframework (every other namespace + deps + Hermes). Called by xcframework.js during compose. This is the alternative header source that lets consumers resolve React Native headers without a clang VFS overlay. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Emit the headers-spec layout unconditionally and delete the VFS overlay across
JS, CI publish, and Ruby. Consumers resolve headers the way the SwiftPM branch
does: <React/...> from the vendored React.framework, every other namespace from
ReactNativeHeaders. No root Headers/ on the xcframework, no VFS.
JS:
- xcframework.js: always emit the React.framework spec layout and build
ReactNativeHeaders.xcframework (was gated behind RN_ZERO_I_LAYOUT=1). Remove
the legacy header path entirely — the podspec->root-Headers enumeration,
createModuleMapFile, and copyHeaderFilesToSlices — so the published
React.xcframework is a standard framework (Info.plist + per-slice
React.framework/{Headers,Modules}), no root Headers/ or Modules/. Ship
ReactNativeHeaders.xcframework inside the reactnative-core tarball (sibling of
React.xcframework) so the prebuilt pod can vend both; keep the standalone
ReactNativeHeaders.xcframework.tar.gz for the SPM path. Drop the
React-VFS-template.yaml emit and the ./vfs import.
- vfs.js: deleted (its only consumer was xcframework.js).
- types.js: drop the now-unused VFSEntry/VFSOverlay/HeaderMapping types.
- replace-rncore-version.js: drop the React-VFS.yaml preservation rationale.
Ruby/CocoaPods:
- React-Core-prebuilt.podspec: vend React.xcframework (its per-slice
React.framework + module map serves <React/...> and @import React via
FRAMEWORK_SEARCH_PATHS); flatten ReactNativeHeaders' headers into a top-level
Headers/ in prepare_command and expose them via the pod header search path.
Drop the VFS-era root header_mappings_dir/module_map.
- rncore.rb: remove the -ivfsoverlay injection and process_vfs_overlay;
add_rncore_dependency and configure_aggregate_xcconfig now add a
HEADER_SEARCH_PATHS to React-Core-prebuilt/Headers for podspec, aggregate, and
third-party targets.
- react_native_pods.rb: drop the process_vfs_overlay post-install call.
Docs: replace the "VFS Overlay System" section with the headers-spec layout;
drop the obsolete "Known Issues" (pre-headers-spec) section.
Verified end-to-end: prebuild compose produces a VFS-free, root-Headers-free
React.xcframework; rn-tester pod install + xcodebuild (prebuilt path) BUILD
SUCCEEDED with zero -ivfsoverlay.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
React.framework is a clang module; when an SPM consumer precompiles it, a
modular React/ header that #imports <react/...> hit
-Wnon-modular-include-in-framework-module because the lowercase react/
namespace (served from ReactNativeHeaders, per R1's Linux/Windows-safe layout)
was deliberately kept out of any module.
Give react/ a module where it already lives instead of relocating it (relocation
would require case-folding react.framework -> React.framework, which only works
on case-insensitive filesystems):
- headers-spec.js: drop the react/ namespace-module exemption so its
objc-modular-candidates get a module; emit that module as
ReactNativeHeaders_react (a module literally named 'react' would alias the
React framework module on a case-insensitive filesystem). Module names are
internal; <react/...> still resolves by header path and is now modular.
- headers-inventory.js: classify C++ default member initializers in aggregates
(e.g. struct { NSString *family = nil; } in RCTFontProperties.h) as ObjC++ so
these are not misclassified objc-modular-candidate and pulled into a plain
ObjC module they cannot compile in.
The unguarded ObjC/C react/ headers (e.g. JSRuntimeFactoryCAPI.h) now resolve
modularly; the C++ react/renderer/* includes are #ifdef __cplusplus-guarded and
skipped during the ObjC module emit, so they need no module.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In prebuilt mode the React core pods' code + headers live entirely in React.xcframework / React-Core-prebuilt. Re-installing their SOURCE podspecs made them ship duplicate headers that shadow the prebuilt artifact and break the React framework's clang explicit-module precompile (-Wnon-modular-include-in-framework-module) under Xcode 26. Install those core pods as dependency-only FACADES instead: generated podspecs with no sources/headers, installed via :path (so nothing is fetched), each depending on React-Core-prebuilt. Version, subspecs, default_subspec and resources (e.g. the privacy manifest) are DERIVED from the real podspec so the facade stays graph- and resource-equivalent to the source pod. With the shadowing gone the React module precompiles cleanly with SWIFT_ENABLE_EXPLICIT_MODULES on, so the Xcode-26 workaround (#53457) is removed. The prebuilt header search path + ReactNativeHeaders module-map activation are consolidated into a single post-install injection site (configure_aggregate_xcconfig); add_rncore_dependency now only declares the React-Core-prebuilt dependency. rn-tester's NativeComponentExample uses the canonical <React/...> include for RCTFabricComponentsPlugins.h (resolved from the framework) so it builds against the facaded React-RCTFabric in prebuilt mode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`yarn format-check` (prettier) was failing CI on PR #57285. Run prettier on the ios-prebuild headers scripts (headers-compose.js, headers-inventory.js), replace-rncore-version.js, and __docs__/README.md so format-check passes. No logic changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…den compose freshness + shell usage
Review feedback on the header modularization (VFS-overlay removal):
- computeInventory() now returns natural-path collisions; computeSpecPlan()
fails closed on them (R8) so two sources projecting to the same Headers/
path can no longer be silently merged (only identities[0].source was kept).
- ensureHeadersLayout()'s freshness marker folds a sha256 of the compose
tooling (headers-{inventory,spec,compose}.js) so a local script edit forces
a recompose — the source xcframework's Info.plist mtime alone couldn't.
- scanHeader() strips /* ... */ block comments (multi-line aware) so a
documentation line mentioning namespace/template/constexpr can't trip the
C++ detector and shrink the umbrella.
- Anonymous aggregates with C++ member initializers (typedef struct { x = ..; })
are now detected as ObjC++ (tag name made optional).
- Multi-hyphen pod names namespaced correctly (.replace(/-/g,'_')).
- cp/tar shell-outs switched to execFileSync with arg arrays (CodeQL
"command from environment values"; parity with headers-verify.js).
- Clearer hermes-missing warning; headers-spec R9/R10 added to the contract
docblock (was R1–R8 then R11).
- New headers-inventory-test.js covering scanHeader (cxx guards, #else/#elif
flips, anonymous aggregate, block/line comments).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
562985d to
3a57ac3
Compare
|
Thanks! Addressed in Blocking ( Other fixes:
Test gap: added |
… sort-imports) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3a57ac3 to
8b50e2c
Compare
/bin/cp -c (APFS clonefile) is macOS-only; Linux CI exercises these paths through the jest integration tests, where GNU cp rejects it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8b50e2c to
ca08afa
Compare
…oolchain calls The xcrun/clang/libtool/lipo/xcodebuild invocations in buildReactNativeHeadersXcframework used execSync with template-interpolated double-quoted paths; a path containing a quote or dollar sign could break or shell-expand. Converted to execFileSync argument arrays (matching headers-verify.js and the existing cp calls); libtool's 2>/dev/null shell redirect becomes stdio: ['ignore', 'pipe', 'ignore']. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…prebuilt/SwiftPM
Source builds get React-Core's non-header resources from the podspec
resource_bundles, but in the prebuilt path the source pods aren't installed
(CocoaPods facades) / not present (SwiftPM), so they were lost. Reproduce them in
the artifact at compose time via scripts/ios-prebuild/framework-resources.js:
- Privacy manifest: merge the PrivacyInfo.xcprivacy of the pods baked into
React.framework into one manifest at the framework root, where Xcode's
privacy-report aggregation picks it up (React.framework is dynamic; no runtime).
- RCTI18nStrings: rebuild RCTI18nStrings.bundle from React/I18n/strings/*.lproj
inside React.framework, resolved at runtime by the framework-aware loader.
RCTLocalizedString.mm now resolves the strings bundle from the code's own bundle
first (React.framework when prebuilt/SwiftPM) with a main-bundle fallback (static
source builds), keeping the graceful nil -> default behaviour.
React-Core.podspec declares RCTI18nStrings and React-Core_privacy in one
resource_bundles map (a later resource_bundles= had silently overwritten the
first), so source builds ship both again. The RNCore facade no longer carries
React-Core_privacy: the prebuilt artifact owns these resources now.
Red/green unit + integration tests in __tests__/framework-resources-test.js.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The FACADE_REEXPOSED_HEADERS loop passed an undefined `podspec_dir` to
copy_reexposed_headers, crashing `pod install` at `use_react_native!`
("undefined local variable or method 'podspec_dir'"). Derive it from the
real podspec path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review feedback on embedding React.framework's non-header resources:
- Privacy-manifest merge no longer fabricates an empty NSPrivacyAccessedAPITypeReasons
key when a source omitted it (tracks per-category key presence), so a single
manifest truly passes through unchanged.
- NSPrivacyCollectedDataTypes dedup canonicalizes (recursively sorts) keys before
hashing, so two pods declaring the same dict in different key order dedup.
- RCTI18nStrings.bundle is now built ONCE into a temp stage and cloned into each
slice (mirrors the privacy manifest) instead of rebuilt inside the slice loop;
i18nLocales is computed once.
- i18n bundle Info.plist gains CFBundleShortVersionString / CFBundleVersion so
Apple validation tooling doesn't warn on a version-less bundle.
- Fixed stale "see" pointers ios-prebuild/{i18n,privacy}.js -> framework-resources.js
(React-Core.podspec + rncore_facades.rb).
- Tests: serialize->readPrivacyManifest round-trip, order-insensitive collected-type
dedup, omitted-reasons-key regression, CFBundleVersion presence, and an
emitReactFrameworkHeaders integration test asserting PrivacyInfo.xcprivacy +
RCTI18nStrings.bundle land in EVERY slice.
Note: resources are written after _CodeSignature is stripped (R7 — signed after
compose), so no signature invalidation; readdirSync({recursive})/cpSync are safe on
the repo's Node >=22 engine.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mixed is now a deprecated utility type (use unknown), and the NSPrivacyAccessedAPITypeReasons refinement was invalidated by the intervening Map.get() call — hoist it into a local first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dle clone Same macOS-only clonefile issue as the other copy sites; this one is on the resource-landing path the jest integration test exercises on Linux. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ca08afa to
7b29638
Compare
The resources shipped inside the prebuilt React.framework fail SILENTLY if the framework is ever consumed without being embedded: every localized string falls back to its untranslated default and the privacy manifest drops out of the app's aggregated privacy report, with no build error. Emit a dev-only, once-per-process RCTLogWarn when the bundle resolves nil so an embed regression is observable in every dev console instead of shipping quietly. No behavior change in release (RCT_DEV compiled out) or on the happy path. Not unit-testable (ObjC runtime diagnostics, no jest surface); both RCT_DEV and NDEBUG preprocessing paths compile-verified via clang -fsyntax-only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… manifests collectReactPrivacyManifestPaths merged ANY PrivacyInfo.xcprivacy found recursively under the React privacy roots — a latent trap: a future pod under those roots that ships as its OWN framework would silently have its manifest folded into React.framework's aggregate (over-declaration in the app's privacy report). The scan is now validated against an explicit REACT_PRIVACY_MANIFESTS allowlist and FAILS the prebuild on an unlisted manifest, with instructions for both resolutions. Listed-but-absent stays legal: partial fixture trees keep working, an upstream deletion under-declares exactly as a source build would, and a MOVED manifest cannot slip through since its new path is unlisted. Also: only create the i18n temp stage when there are .lproj dirs to bundle, and document that the dedup key's `?? ''` is Flow appeasement (JSON.stringify is typed `string | void`), not dead code. 56 ios-prebuild tests green (drift-gate cases red-first); Flow + prettier clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D111448862. |
…encies pod via dependency-only facades (#57440) Summary: Step 1 of making the prebuilt `ReactNativeDependencies` pod the **single header authority** for the third-party C/C++ deps (RCT-Folly, glog, boost, DoubleConversion, fmt, fast_float, SocketRocket) in prebuilt-deps mode. Today the deps **binary** replaces the source pods' code, but the pod still `s.dependency`'s the real source pods and borrows their headers via `$(PODS_ROOT)/<pod>` search paths. That split header authority is the dual-copy bug class behind the 2026-07-03 SocketRocket regression (`duplicate interface` under `use_frameworks!` — SocketRocket's ObjC headers have no include guards). Three commits: 1. **fix(cocoapods): harden prebuilt-deps header search paths and artifact handling** — `rndependencies.rb`'s `||= [] << path` only added the deps header search path when `HEADER_SEARCH_PATHS` was unset (silently dropped otherwise); normalize and always append, and point at the pod-local flattened `Headers/`. `ReactNativeDependencies.podspec` `prepare_command` now fails closed (`exit 1`) instead of silently producing a no-link pod. `reactNativeDependencies.js` no longer deletes + re-downloads a locally staged artifact that lacks a version marker. 2. **feat(cocoapods): dependency-only facades for third-party pods in prebuilt-deps mode** — in prebuilt-deps mode the real source pods are not declared, so a community podspec's hardcoded `s.dependency "RCT-Folly"` would resolve from trunk and compile from source next to the prebuilt binary. `RNDepsFacades` generates dependency-only local facade podspecs (no sources, no headers, a single dependency on `ReactNativeDependencies`); versions/subspecs are derived from the real podspecs in `third-party-podspecs/` (SocketRocket synthesized fail-closed from `socket_rocket_config`). Full contract documented in `scripts/cocoapods/__docs__/prebuilt-deps.md`. 3. **feat(ios-prebuild): SocketRocket privacy manifest + Xcode 26 header layout** — embed an RN-authored, accurate-empty `PrivacyInfo.xcprivacy` for SocketRocket (upstream ships none), and stage flat public headers into `include/` so Xcode 26's SwiftPM accepts the header layout. Stacked on #57305 (base: `chrfalch/prebuilt-resources`); the SwiftPM preview (#57332) rebases on top of this. Follow-up (separate PR): headers-only `ReactNativeDependenciesHeaders.xcframework` sidecar so SPM auto-serves the deps namespaces and `ReactNativeHeaders` goes pure-RN. ## Changelog: [IOS][CHANGED] - Prebuilt-deps mode: serve third-party headers from the ReactNativeDependencies pod itself and resolve community `s.dependency` on RCT-Folly/glog/boost/etc. via dependency-only facade pods Pull Request resolved: #57440 Test Plan: E2E matrix (2026-07-06, locally built deps artifact via `prepare-ios-prebuilds.js`): - rn-tester, prebuilt core + prebuilt deps, static linkage — builds - rn-tester, prebuilt core + prebuilt deps, `USE_FRAMEWORKS=dynamic` — builds (the SocketRocket-regression config) - helloworld (private), prebuilt core + prebuilt deps, static — builds - source-mode control: no facades generated, `Podfile.lock` identical to baseline 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed By: fabriziocucci Differential Revision: D111449257 Pulled By: cipolleschi fbshipit-source-id: ace5716868d126a08721200efd640e903b191658
Summary:⚠️ This is a stacked PR — review/merge the base PRs first Builds on, and should land after (bottom-up): 1. #57285 — Remove the Clang VFS overlay / modularize React headers (base of the stack). <React/…> via the framework module map; lowercase namespaces (react/, yoga/, jsi/, …) via the ReactNativeHeaders module map. 2. #57305 — Prebuilt artifact resources + ReactNativeHeaders.xcframework. Ships the prebuilt core artifacts (incl. ReactNativeHeaders in the tarball + embedded React.framework resources) and the CocoaPods React-Core-prebuilt facades. 3. #57440 — Self-serving prebuilt ReactNativeDependencies + dependency-only facades. The deps pod becomes the single header authority for the third-party namespaces (RCT-Folly/glog/boost/…); community `s.dependency "RCT-Folly"` resolves to local facades instead of trunk source pods. 4. #57442 — ReactNativeDependenciesHeaders sidecar + pure-RN ReactNativeHeaders (immediate base). The deps prebuild emits a headers-only LIBRARY-type sidecar (the binary deps xcframework is framework-type — invisible to SwiftPM binaryTargets), ReactNativeHeaders drops the third-party namespaces, and both headers artifacts publish standalone to Maven. The whole stack is rebased onto current main (2026-07-06). This PR consumes the final **five-artifact set**: React, ReactNativeHeaders (pure-RN), ReactNativeDependencies, ReactNativeDependenciesHeaders, hermes-engine. Adds npx react-native spm and the package-generation tooling that turns an app into a SwiftPM-integrated RN app using the base stack's prebuilt XCFrameworks. CocoaPods stays supported — additive, opt-in, no Ruby toolchain. Integration is injected into the existing .xcodeproj in place (nothing generated/renamed/replaced), recorded in .spm-injected.json so it reverses exactly. ### What this PR adds - The spm CLI (add/update/deinit/scaffold + hidden sync/codegen/download), zero-arg auto-resolution, --deintegrate for CocoaPods→SwiftPM. - SwiftPM package generation (scripts/spm/): autolinking→Package.swift, Codegen→React-GeneratedCode, core XCFramework binary targets, artifact download/cache, surgical pbxproj inject/remove. - ReactNativeDependenciesHeaders wired as the 5th binaryTarget: the headers-only companions (ReactNativeHeaders from the core tarball, the deps sidecar from the deps tarball) are staged automatically out of their parent tarballs — no --headers-tarball priming; REQUIRED_ARTIFACTS covers all five. - Community-library scaffolding from podspecs (incl. root-level-source podspecs via generated include/<SwiftName>/ shims, sibling wiring for transitive spm.dependencies, self-ingestion guards). - In-place Xcode integration (XCLocalSwiftPackageReference + Sync build phase + scheme pre-action + auto-sync; ${PODS_ROOT}-anchored REACT_NATIVE_PATH replaced on --deintegrate with exact deinit rollback). - rn-tester + helloworld SwiftPM consumption + test library; npm-packaging hygiene; a small RNCoreFacades.podspec_dir fix. ## Documentation - **Tool docs** (usage, quick start, architecture): [`scripts/spm/__doc__/spm-scripts.md`](https://github.com/react/react-native/blob/chrfalch/swift-package-manager/packages/react-native/scripts/spm/__doc__/spm-scripts.md) - **Header-paths contract** (how the five artifacts serve headers): [`scripts/spm/__doc__/spm-header-paths-contract.md`](https://github.com/react/react-native/blob/chrfalch/swift-package-manager/packages/react-native/scripts/spm/__doc__/spm-header-paths-contract.md) - **RFC** (updated): [react-native-community/discussions-and-proposals#994](react-native-community/discussions-and-proposals#994) ## Changelog: [IOS] [ADDED] - `npx react-native spm` command + SwiftPM package-generation tooling (opt-in; CocoaPods stays supported) Pull Request resolved: #57332 Test Plan: 350 scripts/spm unit tests (incl. byte-identical add→deinit round-trip); manual E2E against the five-artifact set: fresh app via `cli init`→`spm add --deintegrate`→build (artifact resources verified in the app), rn-tester in-place migration (RNTesterPods.xcodeproj, test libraries + spmModules), helloworld in-place migration — all BUILD SUCCEEDED. Verified `npx react-native spm <cmd>` from the command line in each journey. ## Scope & limitations iOS, prebuilt-only (no build-from-source yet); full spm.xcframework/spm.source library metadata not yet (app-local spm.modules + scaffolding are); Expo not yet. ## Follow-ups Remote-mode ReactNative Package.swift must vend the ReactNativeDependenciesHeaders product (spm-distribution repo); library self-containment for repo-portable manifests; build-from-source. Reviewed By: mdvacca Differential Revision: D111449548 Pulled By: cipolleschi fbshipit-source-id: 87a873bf5c8be2d60f7893611e16924bfa730a46
|
@cipolleschi merged this pull request in 77b7512. |
…Frameworks (#57305) Summary: **Depends on #57285 In source builds, `React-Core` ships non-header resources — its privacy manifest (`PrivacyInfo.xcprivacy`) and its localized strings (RCTI18nStrings) — via the podspec's resource_bundles. In the prebuilt path those source pods aren't installed (CocoaPods facades) or aren't present at all (SwiftPM), so these resources were silently dropped: prebuilt/SwiftPM apps shipped no React Native privacy manifest, and localized strings were unavailable. This embeds them in React.framework at prebuild time so they ship uniformly across CocoaPods-prebuilt and SwiftPM, with source builds unchanged: - **Privacy manifest** — the PrivacyInfo.xcprivacy of the pods baked into React.framework are merged into one manifest at the framework root. React.framework is a dynamic framework, so Xcode's privacy-report aggregation picks it up automatically (no runtime involvement). - **RCTI18nStrings** — rebuilt as RCTI18nStrings.bundle inside React.framework. RCTLocalizedString.mm now resolves the bundle from its own framework first (bundleForClass:), falling back to the app's main bundle for static source builds. It also fixes a latent bug in `React-Core.podspec`: a later resource_bundles = was overwriting the earlier resource_bundle =, so source builds had stopped shipping RCTI18nStrings. Both bundles are now declared together. With the artifact owning these resources, the prebuilt RNCore facade no longer carries them. ## Changelog: [IOS][FIXED] - Ship React-Core's privacy manifest and localized strings (RCTI18nStrings) inside the prebuilt React.xcframework, so CocoaPods-prebuilt and SwiftPM apps include them Pull Request resolved: #57305 Test Plan: - ✅ yarn jest packages/react-native/scripts/ios-prebuild — unit + integration tests for the merge/discovery/bundle-build logic (red/green). - ✅ Built React.xcframework from this branch and confirmed each slice's React.framework carries the merged PrivacyInfo.xcprivacy and RCTI18nStrings.bundle (37 locales). - ✅ Cold-built rn-tester in prebuilt mode and verified the app bundle contains Frameworks/React.framework/{PrivacyInfo.xcprivacy, RCTI18nStrings.bundle}. - ✅ Confirmed React-Core.podspec now reports both resource bundles (RCTI18nStrings, React-Core_privacy) for source builds. Reviewed By: fabriziocucci Differential Revision: D111448862 Pulled By: cipolleschi fbshipit-source-id: a95360f51cc1131510ef03f7e48c689575d1c22d
…encies pod via dependency-only facades (#57440) Summary: Step 1 of making the prebuilt `ReactNativeDependencies` pod the **single header authority** for the third-party C/C++ deps (RCT-Folly, glog, boost, DoubleConversion, fmt, fast_float, SocketRocket) in prebuilt-deps mode. Today the deps **binary** replaces the source pods' code, but the pod still `s.dependency`'s the real source pods and borrows their headers via `$(PODS_ROOT)/<pod>` search paths. That split header authority is the dual-copy bug class behind the 2026-07-03 SocketRocket regression (`duplicate interface` under `use_frameworks!` — SocketRocket's ObjC headers have no include guards). Three commits: 1. **fix(cocoapods): harden prebuilt-deps header search paths and artifact handling** — `rndependencies.rb`'s `||= [] << path` only added the deps header search path when `HEADER_SEARCH_PATHS` was unset (silently dropped otherwise); normalize and always append, and point at the pod-local flattened `Headers/`. `ReactNativeDependencies.podspec` `prepare_command` now fails closed (`exit 1`) instead of silently producing a no-link pod. `reactNativeDependencies.js` no longer deletes + re-downloads a locally staged artifact that lacks a version marker. 2. **feat(cocoapods): dependency-only facades for third-party pods in prebuilt-deps mode** — in prebuilt-deps mode the real source pods are not declared, so a community podspec's hardcoded `s.dependency "RCT-Folly"` would resolve from trunk and compile from source next to the prebuilt binary. `RNDepsFacades` generates dependency-only local facade podspecs (no sources, no headers, a single dependency on `ReactNativeDependencies`); versions/subspecs are derived from the real podspecs in `third-party-podspecs/` (SocketRocket synthesized fail-closed from `socket_rocket_config`). Full contract documented in `scripts/cocoapods/__docs__/prebuilt-deps.md`. 3. **feat(ios-prebuild): SocketRocket privacy manifest + Xcode 26 header layout** — embed an RN-authored, accurate-empty `PrivacyInfo.xcprivacy` for SocketRocket (upstream ships none), and stage flat public headers into `include/` so Xcode 26's SwiftPM accepts the header layout. Stacked on #57305 (base: `chrfalch/prebuilt-resources`); the SwiftPM preview (#57332) rebases on top of this. Follow-up (separate PR): headers-only `ReactNativeDependenciesHeaders.xcframework` sidecar so SPM auto-serves the deps namespaces and `ReactNativeHeaders` goes pure-RN. ## Changelog: [IOS][CHANGED] - Prebuilt-deps mode: serve third-party headers from the ReactNativeDependencies pod itself and resolve community `s.dependency` on RCT-Folly/glog/boost/etc. via dependency-only facade pods Pull Request resolved: #57440 Test Plan: E2E matrix (2026-07-06, locally built deps artifact via `prepare-ios-prebuilds.js`): - rn-tester, prebuilt core + prebuilt deps, static linkage — builds - rn-tester, prebuilt core + prebuilt deps, `USE_FRAMEWORKS=dynamic` — builds (the SocketRocket-regression config) - helloworld (private), prebuilt core + prebuilt deps, static — builds - source-mode control: no facades generated, `Podfile.lock` identical to baseline 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed By: fabriziocucci Differential Revision: D111449257 Pulled By: cipolleschi fbshipit-source-id: ace5716868d126a08721200efd640e903b191658
…S overlay ## Summary In RN 0.87 we'll hopefully release a new version of our precompiled binaries that works without the VFS overlay: - react/react-native#57285 - removal of VFS overlay - react/react-native#57305 - moved resource bundles to xcframework Meaning that we'll get completely self-contained XCFrameworks from RN which is a prerequisite for SwiftPM to not become too complex. ## How This PR addds support for detecting wether we're running with an RN version that uses these new frameworks or not - making sure we fallback to the old version when running with pre 0.87. In addition it updates the pod install pipeline with the same tests. NOTE: When merging 0.87 into Expo (as our current React Native), we can remove a lot of code in a follow-up PR that removes the VFS support all-over. There are a couple of 3rd party packages not compatible with RN 0.87 yet, so I tested with some patches for these packages. In addition a small binary fix was added to fix a missing method in 0.87 in EXpo's code. ## Test-plan - ✅ Built Bare-Expo, minimal-tester and precompiled with the new binaries from React native - ✅ Verified that the same versions are using VFS and precompiling correctly when using RN 0.86
…S overlay ## Summary In RN 0.87 we'll hopefully release a new version of our precompiled binaries that works without the VFS overlay: - react/react-native#57285 - removal of VFS overlay - react/react-native#57305 - moved resource bundles to xcframework Meaning that we'll get completely self-contained XCFrameworks from RN which is a prerequisite for SwiftPM to not become too complex. ## How This PR addds support for detecting wether we're running with an RN version that uses these new frameworks or not - making sure we fallback to the old version when running with pre 0.87. In addition it updates the pod install pipeline with the same tests. NOTE: When merging 0.87 into Expo (as our current React Native), we can remove a lot of code in a follow-up PR that removes the VFS support all-over. There are a couple of 3rd party packages not compatible with RN 0.87 yet, so I tested with some patches for these packages. In addition a small binary fix was added to fix a missing method in 0.87 in EXpo's code. ## Test-plan - ✅ Built Bare-Expo, minimal-tester and precompiled with the new binaries from React native - ✅ Verified that the same versions are using VFS and precompiling correctly when using RN 0.86
Summary
Depends on #57285
In source builds,
React-Coreships non-header resources — its privacy manifest (PrivacyInfo.xcprivacy) and its localized strings (RCTI18nStrings) — via the podspec's resource_bundles. In the prebuilt path those source pods aren't installed (CocoaPods facades) or aren't present at all (SwiftPM), so these resources were silently dropped: prebuilt/SwiftPM apps shipped no React Native privacy manifest, and localized strings were unavailable.This embeds them in React.framework at prebuild time so they ship uniformly across CocoaPods-prebuilt and SwiftPM, with source builds unchanged:
It also fixes a latent bug in
React-Core.podspec: a later resource_bundles = was overwriting the earlier resource_bundle =, so source builds had stopped shipping RCTI18nStrings. Both bundles are now declared together. With the artifact owning these resources, the prebuilt RNCore facade no longer carries them.Changelog:
[IOS][FIXED] - Ship React-Core's privacy manifest and localized strings (RCTI18nStrings) inside the prebuilt React.xcframework, so CocoaPods-prebuilt and SwiftPM apps include them
Test Plan