Christian Falch 24353daa87 fix(iOS): keep prebuilt Headers/ in place on a Debug/Release swap (#57814)
Summary:
Fixes https://github.com/react/react-native/issues/57803. An iOS Release build can fail in `PrecompileModule React` with seven `include of non-modular header inside framework module` errors — but only when the build follows a Debug/Release configuration switch.

`replace-rncore-version.js` deleted and recreated `Pods/React-Core-prebuilt/Headers/` on a swap. That directory holds `module.modulemap`, which `rncore.rb` activates on every target through `-fmodule-map-file`. Nothing orders an unrelated target's dependency scan against this script phase, so a scan can run while the module map is missing. The React module is then precompiled without it, and `<yoga/...>`, `<react/...>` and `<RCTDeprecation/...>` resolve non-modularly.

Those headers never needed replacing. The prebuild compose job emits one set of ReactNativeHeaders for both configurations, so they are identical in the Debug and Release tarballs — only the compiled framework differs. This replaces `React.xcframework` and nothing else.

## Changelog:

[IOS] [FIXED] - Keep the prebuilt `Headers/` in place on a Debug/Release configuration switch so the React explicit module still resolves its module map

Pull Request resolved: https://github.com/react/react-native/pull/57814

Test Plan:
The premise, on the published 0.87.0-rc.3 artifacts (`ios-arm64_x86_64-simulator`):

| compared between the Debug and Release tarballs | result |
| --- | --- |
| `ReactNativeHeaders…/Headers/module.modulemap` | identical |
| `React.framework/Modules/module.modulemap` | identical |
| `ReactNativeHeaders…/Headers` tree (`diff -rq`) | 0 differences |
| `React.framework/Headers` tree (`diff -rq`) | 0 differences |

The reproducer from https://github.com/react/react-native/issues/57803, on Xcode 26.3 with CocoaPods 1.15.2:

| build | result |
| --- | --- |
| 0.87.0-rc.3 | **FAIL** — exit 65, 7 errors |
| 0.87.0-rc.3 + this PR | **PASS** — `** BUILD SUCCEEDED **`, 0 errors |

The swap still does its job in the passing build — it logs `Replacing React-Core-prebuilt/React.xcframework`, and the installed binary is the Release one:

```
installed:   55225ccbc283c57c614ff4caf263cb63bad3828240e62cee8893e7001774bd6c
rc3 release: 55225ccbc283c57c614ff4caf263cb63bad3828240e62cee8893e7001774bd6c
rc3 debug:   516215801a6f8a86640aae13c2f2de1bbdb95189edf124e208f528b1497c7e4c
```

A Release→Debug swap was verified the same way. Across a swap, `Headers/module.modulemap` keeps its inode while `React.xcframework` gets a new one.

## Unit tests

Adds a unit test for the script, 4 cases: correct framework installed, `Headers/module.modulemap` untouched, an Expo-generated `React-use-frameworks.modulemap` left in place, and a fail-closed case on a tarball with no `React.xcframework`. The script needed a `require.main === module` guard and one export to be importable.

```
js1 test xplat/js/react-native-github/packages/react-native/scripts/__tests__/replace-rncore-version-test.js
→ 4 passed, 4 total
```

The module-map case is a real regression test, not just a pin. Restoring the pre-fix delete-and-recreate makes it fail on the inode assertion while the other three keep passing:

```
✕ leaves Headers/module.modulemap untouched
  Expected: 735095485
  Received: 735095515
```

That only works because the fixture tarball also ships `ReactNativeHeaders.xcframework`. Without it the pre-fix code throws its fail-closed error before reaching the assertion, so the test would go red for the wrong reason and would not actually be guarding #57803.

The Expo case covers behaviour this diff removes the explicit protection for. The old save-and-restore of `React-use-frameworks.modulemap` (ada39220a0) is unnecessary now that only `React.xcframework` is replaced, but nothing else pins it.

## Argument parsing

`yargs` parsing moved inside the `require.main === module` guard, so importing the module no longer parses `process.argv`. Verified in both directions.

The command line still performs the swap end to end:

```
$ node replace-rncore-version.js -c Release -r <version> -p <podsRoot>
Replacing React-Core-prebuilt/React.xcframework
Updating React-Core-prebuilt/.last_build_configuration with Release
Done replacing React Native prebuilt

installed binary: binary-Release
module.modulemap inode before=735141703 after=735141703
last_build marker: Release
```

Importing with hostile argv (`-c` collides with jest's `--config`) has no side effects:

```
$ node -e "process.argv = ['node','jest','-c','jest.config.js','--version']; require('./replace-rncore-version.js')"
imported OK, exports: replaceRNCoreConfiguration
```

`arc lint` is clean on both files.

Reviewed By: zeyap

Differential Revision: D114735639

Pulled By: fabriziocucci

fbshipit-source-id: 35ead7dae9ce7ad7160005a15ecb3975817ae728
2026-08-04 20:20:03 +00:00
2026-02-12 04:25:14 -08:00
2025-07-15 10:10:35 -07:00
2026-01-06 03:23:09 -08:00
2026-07-08 09:06:00 +00:00
2026-06-04 10:12:52 -07:00
2026-02-10 07:41:08 -08:00
2026-07-27 15:24:12 +00:00

React Native

Learn once, write anywhere:
Build mobile apps with React.

React Native is released under the MIT license. Current npm package version. PRs are welcome! Follow @reactnative on X Follow @reactnative.dev on Bluesky

Getting Started · Learn the Basics · Showcase · Contribute · Community · Support

React Native brings React's declarative UI framework to iOS and Android. With React Native, you use native UI controls and have full access to the native platform.

  • Declarative. React makes it painless to create interactive UIs. Declarative views make your code more predictable and easier to debug.
  • Component-Based. Build encapsulated components that manage their state, then compose them to make complex UIs.
  • Developer Velocity. See local changes in seconds. Changes to JavaScript code can be live reloaded without rebuilding the native app.
  • Portability. Reuse code across iOS, Android, and other platforms.

React Native is developed and supported by many companies and individual core contributors. Find out more in our ecosystem overview.

Contents

📋 Requirements

React Native apps may target iOS 15.1 and Android 7.0 (API 24) or newer. You may use Windows, macOS, or Linux as your development operating system, though building and running iOS apps is limited to macOS. Tools like Expo can be used to work around this.

🎉 Building your first React Native app

Follow the Getting Started guide. The recommended way to install React Native depends on your project. Here you can find short guides for the most common scenarios:

📖 Documentation

The full documentation for React Native can be found on our website.

The React Native documentation discusses components, APIs, and topics that are specific to React Native. For further documentation on the React API that is shared between React Native and React DOM, refer to the React documentation.

The source for the React Native documentation and website is hosted on a separate repository, @facebook/react-native-website.

🚀 Upgrading

Upgrading to new versions of React Native may give you access to more APIs, views, developer tools, and other goodies. See the Upgrading Guide for instructions.

React Native releases are discussed in this discussion repo.

👏 How to Contribute

The main purpose of this repository is to continue evolving React Native core. We want to make contributing to this project as easy and transparent as possible, and we are grateful to the community for contributing bug fixes and improvements. Read below to learn how you can take part in improving React Native.

Code of Conduct

Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read the full text so that you can understand what actions will and will not be tolerated.

Contributing Guide

Read our Contributing Guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to React Native.

Open Source Roadmap

You can learn more about our vision for React Native in the Roadmap.

Good First Issues

We have a list of good first issues that contain bugs which have a relatively limited scope. This is a great place to get started, gain experience, and get familiar with our contribution process.

Discussions

Larger discussions and proposals are discussed in @react-native-community/discussions-and-proposals.

📄 License

React Native is MIT licensed, as found in the LICENSE file.

Languages
C++ 34.7%
Kotlin 21.8%
JavaScript 19.8%
Objective-C++ 9.7%
Objective-C 5.5%
Other 8.4%