You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit e96a2d5
Browse filesBrowse the repository at this point in the historyBrowse files
Make C++ stable API guards textual in the ReactNativeHeaders module map (#59068)
Summary:
Apps that use React Native through Swift Package Manager and define `RN_STRICT_API` (the C++ stable API opt-in, see #59043) cannot build `import React`.
The prebuilt `ReactNativeHeaders.xcframework` ships a module map with one module per namespace. The prebuild makes every header that reaches no C++ a member of that module, and the `react/cxxstableapi` guards qualify because they are pure preprocessor code. So `ReactNativeHeaders_react` lists `FrameworksGuard.h`, `PrivateGuard.h` and `UmbrellaGuard.h` themselves, plus `react/debug/flags.h` and the `JSRuntimeFactory` headers that include them.
Clang compiles module members on their own, using the importing target's `-D` macros, with neither `RN_BUILDING` nor `RN_UMBRELLA_CONTEXT` set. With `RN_STRICT_API` defined, `PrivateGuard.h` and `UmbrellaGuard.h` hit their `#error` while building the module. `import React` needs that module (`RCTHermesInstanceFactory.h` imports `<react/runtime/JSRuntimeFactoryCAPI.h>`), so every Swift file importing React, and every Objective-C++ file importing `<React/...>` with modules on, fails.
This change declares the guards, and every module member that reaches one through unguarded includes, as `textual header` (new rule R13 in `headers-spec.js`). They still belong to the module, so `React.framework` may keep including them without a non-modular include error, but they are expanded at each include site, where the guard sees its real includer. On `main` this covers 8 headers across `ReactNativeHeaders_react`, `ReactCommon` and `reactperflogger`.
Other changes:
- `headers-verify.js` runs the Swift `import React` gate a second time with `RN_STRICT_API` defined. Its module import fixture now picks a non-textual member of each module.
- The R5 comment said `react/` gets no module. It does, named `ReactNativeHeaders_react`.
## Changelog:
[Internal]
Test Plan:
Prebuild end to end (Debug, iOS simulator slice), from `packages/react-native`:
```
RN_DEP_VERSION=nightly node scripts/ios-prebuild.js -s -f Debug
node scripts/ios-prebuild.js -b -f Debug -p ios-simulator
node scripts/ios-prebuild.js -c -f Debug -p ios-simulator
node scripts/ios-prebuild/headers-verify.js --flavor Debug
```
- The composed module map has the 8 `textual header` entries, and `headers-verify.js` reports `ALL GATES PASSED`, including the new strict gate.
RNTester through SwiftPM, with #59043 applied and the artifact above packaged as CI does (`ReactCoreDebug.xcframework.tar.gz`):
```
RN_CORE_TARBALL_PATH=/path/to/ReactCoreDebug.xcframework.tar.gz npx react-native spm download --artifacts /tmp/spm-artifacts
npx react-native spm scaffold --artifacts /tmp/spm-artifacts --download skip || true
RN_STRICT_API=1 npx react-native spm add --deintegrate --artifacts /tmp/spm-artifacts --download skip
xcodebuild -project RNTesterPods.xcodeproj -scheme RNTester -configuration Debug -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17 Pro' build
```
| Scenario | Before | After |
| --- | --- | --- |
| `RN_STRICT_API` on | Fails: `ReactNativeHeaders_react` won't precompile, Swift target fails | Build succeeds, app links |
| `RN_STRICT_API` on, private include in app code | — | error |
| `RN_STRICT_API` on, direct public include | — | error |
| `RN_STRICT_API` on, "for frameworks" include | — | warning |
| `RN_STRICT_API` on, umbrella include | — | builds |
| `RN_STRICT_API` off (`spm update` without it) | Builds | Builds, no guard diagnostics |
The "Before" row was built against the published nightly. Swift with C++ interop also builds with `RN_STRICT_API` once `main`'s `RCTCallInvoker.h` (which now includes `<React/CallInvoker.h>`) is in the artifact.
Not run: Release flavor, device and Mac Catalyst slices. React Native's dependencies and Hermes came from the nightly rather than being built locally.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
This was also tested locally on rn-tester.
Differential Revision: D125444181
Pulled By: coado
0 commit comments