[WIP] feat: enable React Native new architecture - #7465
Conversation
Upgrades react-native 0.79.5 → 0.81.6, react 19.0.0 → 19.1.5, expo SDK 53 → 54, CLI 18 → 20, babel-preset/metro-config 0.79 → 0.81. Native changes: - Android: compile/targetSdk 35 → 36, buildTools 36.0.0, Gradle 8.14.3, MainApplication.kt uses loadReactNative(), AndroidManifest has usesCleartextTraffic, adds edgeToEdgeEnabled=false. - iOS: removes :hermes_enabled / :new_arch_enabled from use_react_native! (no longer accepted by 0.81). Config: - metro.config.js: metro-config/src → metro-config/private/defaults, drops unstable_enablePackageExports override. - src/debugging/network.js: XHRInterceptor moved under devsupport/devmenu. Patches: - react-native: recreated for 0.81.6 with only the cornerCurve continuous fix (old-arch ObjC path); accessibility fix was upstreamed. - expo-linear-gradient: renamed to 15.0.8, content still applies. Keeps newArchEnabled=false.
…iles Pod install output for RN 0.81 + Expo 54. The :new_arch_enabled keyword is still accepted by use_react_native! in 0.81 but defaults to true when omitted, so it needs to be passed explicitly to opt out. Also regenerates Podfile.lock and picks up Expo 54 template changes to Info.plists (RCTNewArchEnabled=false, alphabetical key order) and project.pbxproj (input/output paths on the Expo Configure project script phase).
patch-package failed to parse the previous hunk — the header line counts were off and blank context lines had no leading space, which patch-package's parser treats as end-of-hunk.
RN 0.81 no longer exposes $REACT_NATIVE_PATH to the Xcode build env, so the old Bundle React Native script phase broke with "react-native-xcode.sh: No such file or directory". The sentry-xcode.sh wrapper already invokes react-native-xcode.sh internally, so pass it directly under with-environment.sh without the extra argument layer.
Metro's unstable_enablePackageExports now defaults to true in RN 0.81. Zustand's ESM entry points use import.meta, which Hermes does not support — causing SyntaxError 'import.meta' is currently unsupported at store creation (createRainbowStore, createQueryStore). Add explicit mappings under the package.json react-native field to force Metro to resolve zustand and its subpaths to the CJS files, bypassing the exports map.
…e export condition With zustand@5.0.10, the package's own exports map routes the `react-native` condition to the CJS entry (`./index.js`), so Metro no longer resolves to the ESM files that used import.meta. The manual subpath overrides added in 592d73b to work around v4's exports map are no longer needed.
v1.2.3 overrode setHitSlopRect and referenced removed RN APIs; v2.0.0 drops those for RN 0.80+. Patch file renamed to match the new version — contents are effectively the same minus line-number shifts.
…for RN 0.81 - flash-list: BlankAreaEvent.dispatch references eventData directly, but in RN 0.81's Event<T> base class eventData is protected and must be accessed via getEventData(). New patch file. - react-native-performance: PerformanceMarkerManager's getExportedCustomDirectEventTypeConstants() overrode the base's MutableMap<String, Any> return type. In RN 0.81 the base changed to Map<String, Any> (covariant narrowing). Updated the patch to match. Both hunks ported from #6924.
react-native@0.81.6 ships react-native-renderer built against react@19.1.4. Pinning react to 19.1.5 caused a runtime crash: Incompatible React versions: The 'react' and 'react-native-renderer' packages must have the exact same version. Instead got: - react: 19.1.5 - react-native-renderer: 19.1.4 Downgrading one patch matches the bundled renderer and resolves it.
The internal react-native-sandbox repo has its own RN 0.81 branch. Set SANDBOX_BRANCH so rainbow-scripts' iOS / Android prebuild hooks check that out instead of the default branch — needed until the sandbox branch is merged. Applied to ios-e2e, android-e2e, ios-builds (both install steps), and unit-test workflows.
…on drift
Two Android startup crashes on RN 0.81 caused by libraries reflecting on
internal RN Java fields that were renamed in 0.81's Kotlin rewrite:
- Bump react-native-animateable-text 0.16.0-beta.0 → 0.17.1 to pull in RN
0.81 + old-arch support. The pre-bump build crashed at first text view
creation:
java.lang.NoSuchFieldException: No field mPreparedSpannableText in
class Lcom/facebook/react/views/text/ReactTextShadowNode;
at com.reactnativereanimatedtext.JBTextShadowNode.<clinit>
RN 0.81 renamed the spannable text field; v0.17.1 reads the new one.
- Bump react-native-reanimated 3.19.4 → 3.19.5 (latest 3.x). Picks up
bridge-lifecycle fixes that resolve a -Xcheck:jni abort in debug
builds (NativeProxyCommon.requestRender called on null after JS thread
bounces).
Verified locally with `yarn android` on Pixel 6 emulator + RN 0.81.6 +
Hermes — Wallet Screen renders cleanly after both bumps.
Note: the in-repo InternalModule.java reflection on
NativeModuleRegistry.mModules / ModuleHolder.mModule also needs renaming
to moduleMap / module respectively for SafeWebSocketModule to install on
RN 0.81. Filed separately since the stub committed on develop differs
from the runtime version.
RN 0.81's Metro respects package.json `exports`, routing static `import`
statements to the `.import` condition. `@reservoir0x/reservoir-sdk`
ships a Parcel-bundled `.mjs` there whose mangled named exports
(e.g. `$6496f8ee40d0c4eb$export$5d730b7aed1a3eb0 as createClient`) come
through as `undefined` once Hermes loads the release bundle.
The crash showed up on every cold launch:
[TypeError: Cannot read property 'createClient' of undefined]
at initializeReservoirClient
…which kills the app before the welcome screen mounts, breaking every
E2E test (iOS sim and Android emulator both — same JS root cause). Local
debug builds happen to work because Metro serves a different transform
in dev mode.
Override the resolver for this one package to its CJS file (`./dist/index.js`,
its `main` field). No effect on other packages.
`require.resolve('@reservoir0x/reservoir-sdk/dist/index.js')` and even
`require.resolve('@reservoir0x/reservoir-sdk/package.json')` throw
`ERR_PACKAGE_PATH_NOT_EXPORTED` on Node 22+ because the package's
`exports` field only exposes `.` (no subpath, no `./package.json`).
CI bundle step crashed in `metro.config.js` evaluation, blowing up
`Test Build` before the bundle could be produced.
Compute the path off `__dirname` instead — metro.config.js sits at the
workspace root, so `node_modules/@reservoir0x/reservoir-sdk/dist/index.js`
is the deterministic path. Bypasses Node's exports check entirely.
Rock's remote-build cache fingerprints native files + package.json by default but not `metro.config.js`. Yesterday's reservoir-sdk resolveRequest override never reached CI builds — rock kept serving the older cached `.app` (fingerprint `188295d8...` — pre-fix) because nothing in the fingerprint changed. Add metro.config.js, metro.transform.js, and babel.config.js to extraSources so JS-bundle config changes properly invalidate the cache.
RN 0.81's Metro defaults `unstable_enablePackageExports` to true, which
routes static `import` statements to packages' `import` condition. Many
npm libs ship ESM there (often Parcel-mangled, e.g. @reservoir0x/reservoir-sdk
and any transitive `class X extends Y` deps); when bundled for Hermes,
their named exports come through as `undefined` and the app crashes on
launch:
[TypeError: Cannot read property 'prototype' of undefined]
[TypeError: Cannot read property 'createClient' of undefined]
at initializeReservoirClient
at Root (App.tsx)
…which kills the app before the welcome screen mounts and breaks every
E2E + Perf test on iOS sim and Android (real device + emulator).
Disabling package exports makes Metro fall back to the `main` field
(CJS), matching what RN 0.79 effectively did and what these packages
historically test against. Replaces the per-package reservoir-sdk
override from b05b3d5 / 96e5f1a with a single config flag.
Replaces the global `unstable_enablePackageExports: false` from d50271b (too broad — flips behaviour for every package) with a per-package map of `exact moduleName → CJS filepath` overrides in `metro.config.js`. The RN 0.81 Metro / Hermes interaction crashes on launch when packages ship Parcel-mangled ESM via the `import` condition: their named exports come through as `undefined` in the release bundle. Reservoir-sdk was the visible top-of-stack symptom; the underlying `prototype of undefined` fires deeper in viem (transitively required by reservoir's CJS — but viem itself shares the same export shape). Add packages to `FORCE_CJS_PACKAGES` as we find them. If this list grows much further, reconsider the global flag.
These three packages all ship parallel `_esm/*` and `_cjs/*` (or `dist/esm`/`dist/cjs`) directories with mirrored `import`/`default` conditions. Rainbow imports a handful of subpaths directly (e.g. `viem/accounts`) and many more transitively through reservoir-sdk and WalletConnect, so listing each subpath was unworkable. Replace the static map with a prefix-matched override that resolves each request to the package's `default` (CJS) export by reading the package's `package.json` exports field directly. Bypasses Node 22's strict subpath enforcement by joining against the package root rather than re-resolving the subpath through `require.resolve`. Eliminates 22 ESM module duplicates from the production Metro bundle.
Reservoir-sdk's CJS calls `axios.create()` at top-level, which fires the moment reservoir-sdk loads. Axios's exports map gates the browser-safe build behind a `browser` condition that Metro doesn't add on native platforms (only on `web`), so Metro picks `dist/node/axios.cjs` — which `require()`s `http`, `https`, `url`, `stream`, `zlib`, etc. Those Node builtins resolve to undefined-or-stub in the bundle, and their absence cascades into `Cannot read property 'prototype' of undefined` during axios load — manifesting as a launch crash that short-circuits reservoir-sdk's exports and produces the secondary `createClient of undefined` error users see. Pin `axios` to its browser CJS through FORCE_CJS_PACKAGES; the bundle drops from 24 axios deps to 10 (Node builtins eliminated).
…N 0.81 `RNFSManager.reject(...)` calls `promise.reject(null, ex.getMessage())` for the generic exception path. RN 0.81's `PromiseImpl.reject(code, message)` is annotated `@NonNull` for `code` and trips a `java.lang.NullPointerException: Parameter specified as non-null is null` the moment it's invoked — currently fires on `unlink` during the wallet-creation flow and crashes the app. Pass an "ERROR" code plus the original throwable instead.
The launch crash root-caused to axios picking its Node CJS entry (which require()s http/https/url/stream/zlib). axios 1.10.0+ added a `react-native` export condition that maps to the browser CJS, so forcing axios to ^1.16.0 via yarn resolutions lets Metro's normal resolver pick the right entry. With axios fixed, the speculative viem/ox/abitype prefix overrides and the reservoir-sdk single-file override are no longer needed. Strip the custom FORCE_CJS_PACKAGES map and the resolveToCjs helper entirely; resolveRequest now just delegates to Metro and falls back through the existing recovery chain. axios in the production bundle is now v1.16.0 from `dist/browser/axios.cjs`, with the Node-only deps eliminated.
The hand-rolled `diff -u` patch didn't include the `diff --git` header that patch-package requires to locate the target file, so CI failed postinstall with "ERROR: Failed to apply patch for package react-native-fs". Re-emit the same one-line edit (RNFSManager.reject null code → "ERROR") in patch-package's expected format (`diff --git a/... b/...` + index + function-context hunk header). Verified against a fresh node_modules install of react-native-fs@2.16.6.
PR #7396 bumped @shopify/flash-list to 1.8.3, which carries the BlankAreaEvent.kt fix our patch was applying. patch-package was already skipping our 1.8.2-named patch because the installed version no longer matches; remove the dead file.
… 0.81 On RN 0.81, an Android TextInput with bold weight and size 18 stops honoring includeFontPadding:false once it has content — measured height jumps from ~28px (placeholder-only) to ~40px (with text), making the Send recipient row reflow as the user types. The placeholder path still respects the flag, so only the populated state regresses. Lock the input to the RN 0.79 measurement (28px ≈ 18px font + ~10px Android ascender/caret padding) so both states render identically.
The fingerprint controls only the native build cache; `rock bundle` runs every CI build and produces a fresh JS bundle regardless. Adding metro.config.js / metro.transform.js / babel.config.js to extraSources doesn't keep stale bundles out of CI — the original "stale APK" symptom that motivated this addition was actually a silent fall-through in the metro resolver after a Node 22 ERR_PACKAGE_PATH_NOT_EXPORTED error, fixed in 96e5f1a. Restore extraSources to the develop state.
iOS:
- Restore OpenInRainbow / ShareWithRainbow Info.plist to develop (last
pod-install run reformatted these from 2-space indent to tabs; pure
whitespace, no semantic change).
- Trim Rainbow/Info.plist back to the single RCTNewArchEnabled=false
addition required by RN 0.81's bundle main info dictionary read in
RCTUtils.mm. Remove four alternate-app-icon entries (baggy, chonks,
mog, redacted) that came in via the local internals prebuild hook
during the same pod-install dance and don't belong in this PR.
Patches:
- Drop patches/react-native-reanimated+3.19.4.patch — package.json now
pins 3.19.5 which ships makeSynchronizable / Synchronizable upstream
(verified in node_modules ReanimatedModuleProxySpec.{cpp,h}).
Patch was already a no-op (filename version mismatch makes
patch-package skip it).
- Drop patches/react-native-animateable-text+0.16.0-beta.0.patch —
package.json now pins 0.17.1, which ships the `children`-allowed
AnimateableTextProps shape upstream (verified in node_modules
src/TextProps.tsx and lib/typescript/src/index.d.ts). Same no-op
status pre-deletion.
Add `# PATCH CONTEXT` headers to the six patches affected by the RN 0.81 work, per the Patch Management RFC. Each header captures Why, Upstream Issue, Linear Issue, and Remove when so the patch is self- documenting and tracking is anchored to a Linear ticket. - react-native+0.81.6.patch — FEPLAT-97 - react-native-fs+2.16.6.patch — FEPLAT-93 - @Shopify+react-native-performance+4.1.2.patch — FEPLAT-94 - expo-linear-gradient+15.0.8.patch — FEPLAT-96 - @react-native-menu+menu+2.0.0.patch — FEPLAT-95 - react-native-animateable-text+0.17.1.patch — FEPLAT-98 (new) The animateable-text patch was deleted in d3b7aff on the assumption that the upstream fix in 0.17.1 was complete. Source was fixed but the published `lib/typescript/src/*.d.ts` still ship the old `Omit<TextProps, "children">` shape, so consumers fail TypeScript with TS2322 on `<AnimatedText>{children}</AnimatedText>` (reproduced by `yarn lint:ci` on src/design-system/components/Text/AnimatedText.tsx). Restore the patch against 0.17.1's distributed types until upstream republishes. ios/Podfile.lock: pick up animateable-text 0.16.0-beta.0 → 0.17.1 (was already bumped in package.json/yarn.lock; lockfile was stale).
The previous patch only updated lib/typescript/src/TextProps.d.ts and AnimateableText.web.d.ts, but `package.json#types` points to lib/typescript/src/index.d.ts — which carries the same un-regenerated `Omit<TextProps, "children">` shape and is what TypeScript actually consumes when resolving the package. Local lint passed only because I had hand-edited that file during exploration; CI hit pristine state and TS2322 stuck. Add the third hunk so all three published .d.ts files are aligned with the upstream source. lint:ts now runs clean against pristine node_modules.
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
| - Firebase/Core (12.10.0): | ||
| - React-RCTFabric | ||
| - ReactCommon/turbomodule/core | ||
| - FBLazyVector (0.81.5) |
There was a problem hiding this comment.
Podfile.lock downgrades React Native from 0.81.6 to 0.81.5
High Severity
The committed Podfile.lock reverts all React Native pods from 0.81.6 to 0.81.5, but package.json declares "react-native": "0.81.6". This means pod install would resolve to native code from a different RN version than what the JS bundle expects, risking build failures or runtime crashes from native/JS ABI mismatches. The lockfile appears to have been generated from a stale node_modules state.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ccc63ae. Configure here.
| RNPermissions: | ||
| :path: "../node_modules/react-native-permissions" | ||
| RNReactNativeHapticFeedback: | ||
| :path: "../node_modules/react-native-haptic-feedback" |
There was a problem hiding this comment.
Podfile.lock adds undeclared react-native-haptic-feedback dependency
Medium Severity
The Podfile.lock introduces RNReactNativeHapticFeedback (from react-native-haptic-feedback), but package.json has no such dependency. The PR description explicitly states the haptics swap to react-native-haptic-feedback was "dropped here" in favor of the existing react-native-turbo-haptics. This phantom pod entry confirms the lockfile was generated from a different branch state and would fail pod install since the package isn't in node_modules.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ccc63ae. Configure here.
| - SocketRocket | ||
| - Yoga | ||
| - RNReanimated/reanimated/apple (3.19.5): | ||
| - RNReanimated (4.2.1): |
There was a problem hiding this comment.
Podfile.lock reanimated/worklets versions mismatch package.json and patch
Medium Severity
Podfile.lock resolves RNReanimated to 4.2.1 and RNWorklets to 0.7.1, but package.json declares react-native-reanimated at 4.3.1 and react-native-worklets at 0.8.3. The patch file is named react-native-reanimated+4.3.1.patch. These mismatches mean the stale lockfile would cause pod install to report version conflicts against the actual node_modules contents, and could lead to native/JS API mismatches if somehow resolved.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ccc63ae. Configure here.
The 3.19.4 → 3.19.5 bump dropped patches/react-native-reanimated+3.19.4.patch because the Synchronizable backport it contained is upstream in 3.19.5. That file also carried a ProMotion hunk in REANodesManager.mm — opt into preferredFrameRateRange(80, 120, 120) on iOS 15+ — which got removed by accident in the same delete. Without it, iOS clamps to 60 fps on non-ProMotion devices and never reaches 120 fps on ProMotion ones.
2dd7202 to
0151585
Compare
Flips on the new architecture on top of the RN 0.81 upgrade. Extracted from the WIP attempt in #6924 (RN 0.81 + new arch combined). Architecture flags: - Android `gradle.properties`: `newArchEnabled=true`, adds `reactNativeReleaseLevel=experimental`. - iOS `Podfile`: drops `:new_arch_enabled => false` so use_react_native! defaults to the new arch. - iOS `*/Info.plist`: `RCTNewArchEnabled` flips to `true` and adds `ReactNativeReleaseLevel=experimental` on the main target. - `MainApplication.kt`: sets `DefaultNewArchitectureEntryPoint.releaseLevel = EXPERIMENTAL`. Library / patch swaps required by the new arch: - `react-native-reanimated` 3.19.5 -> 4.2.1 (v3 is old-arch-only). - Adds `react-native-worklets` (companion to reanimated 4); switches `react-native-reanimated/plugin` -> `react-native-worklets/plugin` in `babel.config.js`; Jest `transformIgnorePatterns` includes the package. - Replaces `react-native-text-input-mask` (no new-arch support) with `react-native-advanced-input-mask` in ExchangeInput / GweiInputPill. - `@react-native-masked-view/masked-view` 0.3.2 -> rainbow-me/masked-view fork to fix new-arch layout issues. - Adds `patches/react-native-reanimated+4.2.1.patch`. - `NavbarHeightModule`: refactors @ReactMethod sync getter into a separate `getNavigationBarHeightSync()`. Native component migration to Fabric: - Removes the old-arch `ios/Button.swift` / `ButtonManager.m` (RCTViewManager) and replaces with a codegen'd Fabric component at `src/codegen/specs/NativeButtonNativeComponent.ts` + `src/codegen/ios/Components/Button/ButtonComponentView.mm`. Wired up via a podspec + `react-native.config.js`. `ButtonPressAnimation.ios.tsx` consumes the codegen component directly. - `react-native-cool-modals`: migrates Screen / ScreenStack / Portal from `RCTViewManager` + `RNCMScreen.m` to Fabric component views with ShadowNodes and ComponentDescriptors (`RNCMScreenComponentView.mm`, `RNCMScreenStackComponentView.mm`, `WindowPortalComponentView.mm`, `RNCMTouchHandler.mm`, `RNCMScreenViewController.mm`). Adds `react-native-cool-modals.podspec` and the `specs/` TS specs so the package participates in codegen. - Updates `NativeStackView` / `Portal` / `createNativeStackNavigator` JS to consume the new specs. Other cleanups carried over from #6924: - Drops `src/components/AnimatedComponents/extendedPropsAllowlists.ts` and several `pickAnimatedProps` allowlists - Fabric no longer needs them. - Removes the old `NativeButton.tsx` wrapper that called `requireNativeComponent('RNZoomableButton')`. - iOS bridging header / pbxproj cleanup and `.clang-format` for the new Objective-C++ files.
The split into getNavigationBarHeight + getNavigationBarHeightSync was incidental drift from #6924's older base, not a new-arch requirement. develop intentionally consolidated this in #7317 (the sync method had a stray @ReactMethod exposing it twice).
…ch keys OpenInRainbow / ShareWithRainbow had a 2-space → tab reformat that wasn't related to new arch. Rainbow/Info.plist had stale icon shuffling (added baggy/chonks/redacted, removed rnbw) that was already on develop differently. Reverted all three to #7366's base and re-applied only the new-arch keys: RCTNewArchEnabled=true plus ReactNativeReleaseLevel=experimental on the main target.
…Props.cpp (Fabric) On the new arch we no longer hit the legacy ObjC RCTView path, so the kCACornerCurveContinuous hunk against RCTView.m never applied. Replace it with a Fabric-level fix that flips the default `BorderCurve` resolved value from Circular to Continuous in BaseViewProps so views without an explicit `borderCurve` prop still render with squircle corners.
Reanimated 4.3.1 is the current latest stable. Bumps the companion react-native-worklets to 0.8.3 (4.3.x peer-deps require 0.8.x). The existing patch hunks (REANodesManager ProMotion + colors.ts null check) apply unchanged, so only the patch filename moves.
Documents the two hunks (ProMotion frame range + colors.ts null guard). Confirmed neither is upstream on 4.3.1 stable or 4.4 nightly. Matches the RFC header convention used by the other patches in patches/.
ccc63ae to
e21d014
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 4 total unresolved issues (including 3 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e21d014. Configure here.
| + .borderCurves = borderCurves.resolve(isRTL, BorderCurve::Continuous), | ||
| .borderStyles = borderStyles.resolve(isRTL, BorderStyle::Solid), | ||
| }; | ||
| } |
There was a problem hiding this comment.
Corner curve patch drops legacy interop view coverage
Medium Severity
The RCTView.m patch (legacy rendering path) was entirely replaced by the BaseViewProps.cpp patch (Fabric path), rather than keeping both hunks in the patch file. With new architecture enabled, third-party libraries that haven't migrated to Fabric still render through the legacy interop layer, where RCTView handles its own border rendering independently of BaseViewProps. Those views will lose continuous (squircle) corners and fall back to circular corners—a visual regression. The patch file format supports multiple file hunks, so both the legacy RCTView.m hunk and the new BaseViewProps.cpp hunk can coexist.
Reviewed by Cursor Bugbot for commit e21d014. Configure here.
96b738b to
a5986f9
Compare
a5986f9 to
5f1e108
Compare


Stacked on #7366 (RN 0.81 upgrade). Do not merge until #7366 lands. This re-opens #7463, which auto-closed when its temporary side-PR base branch was deleted.
Description
#7366 upgrades to RN 0.81 with
newArchEnabled=falseon both platforms — the upgrade and the architecture flip were deliberately separated. This PR is the second half: it flips the new architecture on (Fabric + TurboModules) and brings in the source/native changes from the abandoned WIP attempt in #6924 that are still required to make the app build and run under Fabric on RN 0.81.6.Every change in the diff is in service of that goal. Where #6924 contained incidental drift (it was based on a much older develop), that drift has been left out — only the new-arch-required pieces remain.
Solution
The three architecture switches live in
android/gradle.properties(newArchEnabled=true), the iOSPodfile(drops the explicit:new_arch_enabled => falsesouse_react_native!defaults to true on 0.81), and the per-targetInfo.plistfiles (RCTNewArchEnabled=true). The main iOS target andMainApplication.ktalso opt into the experimental release level since Fabric on 0.81 is still gated as experimental.A handful of libraries don't work on Fabric and have to be swapped:
react-native-reanimated3.19.5 → 4.3.1. Reanimated v3 is old-arch-only; v4 is new-arch-only. v4 splits its runtime into a companion packagereact-native-worklets@0.8.3, so the Babel plugin moves fromreact-native-reanimated/plugintoreact-native-worklets/pluginand Jest'stransformIgnorePatternslets it through the transformer. A patch (patches/react-native-reanimated+4.3.1.patch) carries one local fix.react-native-text-input-mask→react-native-advanced-input-mask@1.4.6.react-native-text-input-maskhas no Fabric support. We attempted this swap previously on the old architecture and backed it out because of regressions, so the two callsites (ExchangeInputandGweiInputPill) need careful review — see the test plan.@react-native-masked-view/masked-view→rainbow-me/masked-viewfork (#new-archbranch). Upstream's Fabric implementation was merged (#221) and then reverted (#222) for a lint failure; the reintroduce PR (#223) was closed and the package overhaul (#246) is paused waiting for an RN-nativemaskattribute that @paradowstack estimates will ship in RN 0.85. RN is on 0.81 today, so the fork is the bridge until then. The fork is the previously-reverted calculate native price from market details of selected swap input #221 implementation rebased onto develop.The RN
cornerCurvepatch needs to move too: #7366 patchesRCTView.mon the legacy ObjC path to forcekCACornerCurveContinuous, but Fabric doesn't go through that code. The patch is rewritten to flip the defaultBorderCurveresolved value fromCirculartoContinuousinBaseViewProps.cpp, which is the equivalent place in the Fabric render path.Two of rainbow's own native components had to be ported to Fabric. The Zoomable Button (used by
ButtonPressAnimationon iOS) was aRCTViewManager-based view inios/Button.swift+ButtonManager.m. It's replaced by a codegen'd Fabric component: a JS spec atsrc/codegen/specs/NativeButtonNativeComponent.ts, an Objective-C++ implementation atsrc/codegen/ios/Components/Button/ButtonComponentView.mm, and a podspec (src/codegen/rainbow-codegen.podspec) that ties it into the iOS build.ButtonPressAnimation.ios.tsxnow consumes the codegen'd component directly, and theNativeButton.tsxwrapper aroundrequireNativeComponent('RNZoomableButton')is gone. The same migration is done forreact-native-cool-modals:RNCMScreen,RNCMScreenStack, andWindowPortalmove fromRCTViewManager+RNCMScreen.m/RNCMScreenStack.mto Fabric component views with ShadowNodes, ComponentDescriptors, and a dedicated touch handler. Areact-native-cool-modals.podspecandspecs/Native*.tsmake the package participate in codegen.Finally, a few cleanups become possible once everything is on Fabric:
src/components/AnimatedComponents/extendedPropsAllowlists.tsand thepickAnimatedPropsplumbing it fed are deleted (Fabric handles prop forwarding without an allowlist), the iOS bridging header /project.pbxprojlose entries for the deleted Objective-C files, and a.clang-formatis added for the new Objective-C++ files.#6924 also swapped the haptics library from
react-native-turbo-hapticstoreact-native-haptic-feedback. That swap is dropped here — develop has since standardized onreact-native-turbo-haptics, which already supports the new architecture.Test plan
Local builds haven't been run yet (TODO before un-drafting). When they have:
bundle exec pod installsucceeds, app builds and launches withRCTNewArchEnabled=true.yarn androidbuilds and launches withnewArchEnabled=trueingradle.properties.ButtonPressAnimationtaps feel identical to before — the codegen'd Fabric button should be visually indistinguishable from the oldRCTViewManagerbutton.ExchangeInputandGweiInputPill(high-scrutiny because the prior old-arch attempt regressed): typing, paste, autofocus, IME composition on Android, backspacing across the mask boundary, focus/blur side effects.