Repository navigation
Conversation
|
@coado has exported this pull request. If you are a Meta employee, you can view the originating Diff in D124985657. |
coado
force-pushed
the
export-D124985657
branch
from
October 14, 2026 10:22
afdfbd7 to
bc5bc92
Compare
coado
added a commit
to coado/react-native
that referenced
this pull request
Oct 14, 2026
…EWORKS (react#59043) Summary: Let iOS apps that use the Swift Package Manager setup enforce React Native's C++ stable API, and control its "for frameworks" warning, matching the CocoaPods and Android opt-ins. Two env vars are read by `npx react-native spm add` / `update`: - `RN_STRICT_API=1` defines the `RN_STRICT_API` macro, which arms the `react/cxxstableapi` guards. - `RN_ALLOW_FRAMEWORKS=1` defines the `RN_ALLOW_FRAMEWORKS` macro, which silences their "for frameworks" warning. Each variable is named after the macro it defines, as in the CocoaPods opt-in. Each opted-in macro is defined for the app's own code: - the app target (`GCC_PREPROCESSOR_DEFINITIONS`, seeded with `$(inherited)`), - the packages the autolinker generates for the app's local `spm.modules`, - the app codegen package (`ReactCodegen`, `ReactAppDependencyProvider`). Generated code never warns: the codegen package always defines `RN_ALLOW_FRAMEWORKS`, because nobody can act on a warning in generated code and libraries are held to the API in their own builds. Under SwiftPM every library builds from its own `Package.swift` (shipped upstream or created with `spm scaffold`), so a library declares `RN_ALLOW_FRAMEWORKS` there, and the app's opt-ins do not reach libraries. React Native itself ships prebuilt, so none of its code is compiled with these macros. The Xcode build phase runs `spm sync` without the shell environment, so `add`/`update` record the chosen macros in `.spm-injected.json` and every later sync regenerates the packages from that record. As with `pod install`, each `add`/`update` re-reads the variables: running `update` without one turns its macro off, and `deinit` removes the app-target defines along with the rest of the injection. The SwiftPM guide (`scripts/spm/__docs__/spm-scripts.md`) documents the opt-ins: what they define, that libraries are only checked when they define the macros in their own `Package.swift`, and how the choice is remembered for Xcode builds. The opt-ins are one list (`CXX_API_DEFINES` in `spm-utils.js`) passed through as macro names, so adding another opt-in is a one-line change. Changelog: [iOS][Added] - Add `RN_STRICT_API` and `RN_ALLOW_FRAMEWORKS` opt-ins for the Swift Package Manager setup to enforce React Native's C++ stable API in the app's native code Differential Revision: D124985657
coado
added a commit
to coado/react-native
that referenced
this pull request
Oct 19, 2026
…ap (react#59068) Summary: Apps that use React Native through Swift Package Manager and define `RN_STRICT_API` (the C++ stable API opt-in, see react#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 react#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
…EWORKS (react#59043) Summary: Let iOS apps that use the Swift Package Manager setup enforce React Native's C++ stable API, matching the CocoaPods and Android opt-ins. Two env vars are read by `npx react-native spm add` / `update`: - `RN_STRICT_API=1` defines the `RN_STRICT_API` macro, which arms the `react/cxxstableapi` guards. - `RN_ALLOW_FRAMEWORKS=1` defines the `RN_ALLOW_FRAMEWORKS` macro, which silences their "for frameworks" warning. Each variable is named after the macro it defines, as in the CocoaPods opt-in. Each opted-in macro is defined for the app's own code: - the app target (`GCC_PREPROCESSOR_DEFINITIONS`, seeded with `$(inherited)`), - the packages the autolinker generates for the app's local `spm.modules`, - the app codegen package (`ReactCodegen`, `ReactAppDependencyProvider`). Generated code never warns: the codegen package always defines `RN_ALLOW_FRAMEWORKS`, because nobody can act on a warning in generated code and libraries are held to the API in their own builds. Under SwiftPM every library builds from its own `Package.swift`, so a library declares `RN_ALLOW_FRAMEWORKS` there, and the app's opt-ins do not reach libraries. React Native itself ships prebuilt, so none of its code is compiled with these macros. The Xcode build phase runs `spm sync` without the shell environment, so `add`/`update` record the chosen macros in `.spm-injected.json` and every later sync regenerates the packages from that record. As with `pod install`, each `add`/`update` re-reads the variables: running `update` without one turns its macro off, and `deinit` removes the app-target defines along with the rest of the injection. Changelog: [iOS][Added] - Add `RN_STRICT_API` and `RN_ALLOW_FRAMEWORKS` opt-ins for the Swift Package Manager setup to enforce React Native's C++ stable API in the app's native code Reviewed By: cipolleschi Differential Revision: D124985657
coado
force-pushed
the
export-D124985657
branch
from
October 19, 2026 15:29
bc5bc92 to
c457fd8
Compare
|
This pull request has been merged in 47f744d. |
Collaborator
|
This pull request was successfully merged by @coado in 47f744d When will my fix make it into a release? | How to file a pick request? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
Let iOS apps that use the Swift Package Manager setup enforce React Native's C++ stable API, matching the CocoaPods and Android opt-ins. Two env vars are read by
npx react-native spm add/update:RN_STRICT_API=1defines theRN_STRICT_APImacro, which arms thereact/cxxstableapiguards.RN_ALLOW_FRAMEWORKS=1defines theRN_ALLOW_FRAMEWORKSmacro, which silences their "for frameworks" warning.Each variable is named after the macro it defines, as in the CocoaPods opt-in.
Each opted-in macro is defined for the app's own code:
GCC_PREPROCESSOR_DEFINITIONS, seeded with$(inherited)),spm.modules,ReactCodegen,ReactAppDependencyProvider).Generated code never warns: the codegen package always defines
RN_ALLOW_FRAMEWORKS, because nobody can act on a warning in generated code and libraries are held to the API in their own builds.Under SwiftPM every library builds from its own
Package.swift, so a library declaresRN_ALLOW_FRAMEWORKSthere, and the app's opt-ins do not reach libraries. React Native itself ships prebuilt, so none of its code is compiled with these macros.The Xcode build phase runs
spm syncwithout the shell environment, soadd/updaterecord the chosen macros in.spm-injected.jsonand every later sync regenerates the packages from that record. As withpod install, eachadd/updatere-reads the variables: runningupdatewithout one turns its macro off, anddeinitremoves the app-target defines along with the rest of the injection.Changelog:
[iOS][Added] - Add
RN_STRICT_APIandRN_ALLOW_FRAMEWORKSopt-ins for the Swift Package Manager setup to enforce React Native's C++ stable API in the app's native codeReviewed By: cipolleschi
Differential Revision: D124985657