Commit ff2562e
fix(ios): load RCTDefines.h first in React umbrella + drop submodule wildcard so RCT_* macros stay visible (#56862)
Summary:
When `RCT_USE_PREBUILT_RNCORE=1` (the 0.84+ default) and a consumer pod builds under `use_frameworks!`, RCT macros like `RCT_EXTERN`, `RCT_EXTERN_C_BEGIN`, `RCT_EXPORT_METHOD`, `RCT_REMAP_METHOD` fail to propagate.
The `.m` files using these macros hit cascading parse errors like :
- `unknown type name 'RCT_EXTERN_C_BEGIN'` in `<React/RCTBridgeModule.h>` line 51,
- `unknown type name 'RCT_EXTERN'` in `<React/RCTEventDispatcherProtocol.h>` line 17,
- and `type specifier missing, defaults to 'int'` on every `RCT_EXPORT_METHOD(...)` call site.
Root cause is in two places in `packages/react-native/scripts/ios-prebuild/templates/`:
1. `React-umbrella.h` orders `RCTBridgeConstants.h` (line 56) before `RCTDefines.h` (line 84). `RCTBridgeConstants.h` uses `RCT_EXTERN` at its first declaration but the macro is only defined later in `RCTDefines.h`. When the umbrella is loaded as a single parse context, the `RCT_EXTERN` use is unresolvable.
2. `module.modulemap` declares `module * { export * }` inside both `framework module` blocks, which creates an inferred submodule per header. That isolates macro `#define`s into per-header parse scopes. Even fixing the umbrella order, sibling submodules don't share macros: macros defined in the `RCTDefines.h` submodule are invisible to the `RCTBridgeConstants.h` submodule, and to consumers of the `React` module.
This PR fixes both:
- Move `#import <React/RCTDefines.h>` to the very top of the umbrella (right after the prologue and before any other `<React/...>` import), with a comment explaining why ordering matters.
- Remove the `module * { export * }` wildcard from both `framework module` blocks in the modulemap. The umbrella header alone is sufficient to declare the module's contents, and dropping the wildcard means the whole module shares one parse context where macros propagate.
The combination of `use_frameworks!` + prebuilt RNCore is the path of least resistance today: `use_frameworks!` is the Expo default and is required by `firebase-ios-sdk` for its Swift bridges. Prebuilt RNCore is the 0.84+ default. The colliding defaults break native-module ecosystems like `react-native-firebase` that consume the RCT macros. Producer-side fix is in flight at invertase/react-native-firebase#9024; this PR closes the consumer side.
## Changelog:
[IOS] [FIXED] - Load `RCTDefines.h` first in the prebuilt React umbrella and drop the `module *` submodule wildcard so RCT_* macros propagate to consumer pods under `use_frameworks!`.
Pull Request resolved: #56862
Test Plan:
Reproduced on RN 0.85.3 (latest stable at time of writing) on macOS 26.3.1 / Xcode 26.5 / iOS 26.5 Simulator (iPhone 17 Pro).
**Reproducer setup**
Fork of `mikehardy/rnfbdemo` with these adjustments in `make-demo.sh`:
- `RN_VER=0.85.3`
- `RNFB_VER=24.0.0`
- `FB_IOS_VER=12.10.0`
- Remove `react-native-firebase/dynamic-links` from the bulk yarn add (deprecated and removed in RNFB 24.x).
Default Podfile mods produced by `make-demo.sh` are preserved: `use_frameworks! :linkage => :static` + `$RNFirebaseAsStaticFramework = true`. No `RCT_USE_PREBUILT_RNCORE` override (stays at the `1` default).
Public reproducer: https://github.com/wneel/rnfbdemo (see its `REPRO.md` for the two-stage flow).
The reproducer also has invertase/react-native-firebase#9024 (producer-side podspec fixes) applied as an overlay. Without those, the build fails earlier at `-Wnon-modular-include-in-framework-module`. With them applied, the build progresses to the consumer side and surfaces the macro propagation bug this PR fixes.
**Before this PR**
Same reproducer config. iOS build fails on consumer pods (RNFBAuth, RNFBStorage, RNFBMessaging) with:
```
.../Pods/Headers/Public/React-Core/React/RCTEventDispatcherProtocol.h:17:1: error: unknown type name 'RCT_EXTERN'
.../Pods/Headers/Public/React-Core/React/RCTEventDispatcherProtocol.h:17:27: error: expected ';' after top level declarator
.../node_modules/react-native-firebase/messaging/ios/RNFBMessaging/RNFBMessaging+AppDelegate.h:26:21: error: declaration of 'RCTPromiseRejectBlock' must be imported from module 'RNFBApp.RNFBAppModule' before it is required
```
**After this PR**
Same reproducer config builds cleanly: `pod install` clean, `xcodebuild` Debug build produces the app, zero `RCT_EXTERN` macro errors anywhere in the log.
Verified by editing `packages/react-native/scripts/ios-prebuild/templates/React-umbrella.h` and `packages/react-native/scripts/ios-prebuild/templates/module.modulemap` in place (the diff in this PR), regenerating the prebuilt React.xcframework artifact, and re-running the reproducer.
**Output of `npx react-native-community/cli info` from the reproducer environment:**
```
System:
OS: macOS 26.3.1
CPU: (10) arm64 Apple M2 Pro
Memory: 466.16 MB / 16.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.14.0
path: /usr/local/bin/node
Yarn:
version: 3.6.4
path: /usr/local/bin/yarn
npm:
version: 11.12.1
path: /usr/local/bin/npm
Watchman:
version: 2026.05.11.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.16.2
path: /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 25.5
- iOS 26.5
- macOS 26.5
- tvOS 26.5
- visionOS 26.5
- watchOS 26.5
IDEs:
Xcode:
version: 26.5/17F42
path: /usr/bin/xcodebuild
Languages:
Java:
version: 21.0.9
path: /usr/bin/javac
```
**Cross-references**
- `react-native-firebase` producer-side fix in flight: invertase/react-native-firebase#9024
- `react-native-firebase` `.m` import hygiene follow-up: invertase/react-native-firebase#9026
- Original RNFB user-facing issue: invertase/react-native-firebase#8883
Reviewed By: cortinico
Differential Revision: D105582598
Pulled By: cipolleschi
fbshipit-source-id: ab364d46fc94d64274197cdd717a06e425a9324f1 parent aa91ba2 commit ff2562e
2 files changed
Lines changed: 5 additions & 3 deletions
Lines changed: 5 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
20 | 25 | | |
21 | 26 | | |
22 | 27 | | |
| |||
81 | 86 | | |
82 | 87 | | |
83 | 88 | | |
84 | | - | |
85 | 89 | | |
86 | 90 | | |
87 | 91 | | |
| |||
Lines changed: 0 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | 4 | | |
6 | 5 | | |
7 | 6 | | |
8 | 7 | | |
9 | 8 | | |
10 | 9 | | |
11 | | - | |
12 | 10 | | |
0 commit comments