Skip to content

Commit 50c58a0

Browse files
committed
feat(messaging)!: migrate messaging to TurboModules
Convert the messaging native module to a Codegen TurboModule shell while keeping the legacy event-emitter path in place per NewArch-AD-4. Method calls now route through NativeRNFBTurboMessaging (iOS .mm shell, Android spec-backed shell); event delivery continues over the app-module proxy so foreground, background, and headless message handling are unchanged. Add committed codegen artifacts, register messaging in codegen:verify and the spec-native parity list, and remove the legacy Android module. BREAKING CHANGE: messaging requires the React Native New Architecture.
1 parent 35a4667 commit 50c58a0

35 files changed

Lines changed: 2446 additions & 247 deletions

jest.setup.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,12 +401,16 @@ jest.doMock('react-native', () => {
401401
getToken: jest.fn(),
402402
deleteInstallations: jest.fn(),
403403
},
404-
RNFBMessagingModule: {
404+
NativeRNFBTurboMessaging: {
405+
getConstants: () => ({
406+
isAutoInitEnabled: true,
407+
isDeliveryMetricsExportToBigQueryEnabled: false,
408+
isRegisteredForRemoteNotifications: false,
409+
isNotificationDelegationEnabled: false,
410+
}),
405411
isAutoInitEnabled: true,
406412
isDeliveryMetricsExportToBigQueryEnabled: false,
407413
isRegisteredForRemoteNotifications: false,
408-
isNotificationDelegationEnabled: false,
409-
onMessage: jest.fn(),
410414
completeNotificationProcessing: jest.fn(),
411415
setAutoInitEnabled: jest.fn(),
412416
getInitialNotification: jest.fn(() => Promise.resolve(null)),
@@ -425,6 +429,7 @@ jest.doMock('react-native', () => {
425429
subscribeToTopic: jest.fn(),
426430
unsubscribeFromTopic: jest.fn(),
427431
setDeliveryMetricsExportToBigQuery: jest.fn(),
432+
isNotificationDelegationEnabled: jest.fn(() => Promise.resolve(false)),
428433
setNotificationDelegationEnabled: jest.fn(),
429434
},
430435
NativeRNFBTurboPerf: {

okf-bundle/new-architecture/migration-work-queue.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ timestamp: 2026-06-26T00:00:00Z
88

99
# TurboModule migration — work queue
1010

11-
> **IN PROGRESS (2026-07-02):** Phase **4** `messaging` TurboModule migration. P4a: **defer** events to Phase C ([ADR-4](architecture-decisions.md#newarch-ad-4--events-deferred-to-phase-c--accepted)).
11+
> **IN PROGRESS (2026-07-02):** Phase **4** **next pickup:** `database` + remaining Tier A/B. P4m committed; P4a events deferred to Phase C ([ADR-4](architecture-decisions.md#newarch-ad-4--events-deferred-to-phase-c--accepted)).
1212
> **Goal/order:** app foundation → hard probe → easy wins → remaining complex → sync conversion → coordinated break → cleanup (events, shared-state encapsulation). Decisions: [architecture-decisions.md](architecture-decisions.md). Links: [implementation workflow](turbomodule-implementation-workflow.md), [change authoring](../testing/change-authoring-workflow.md), [functions reference](../../../packages/functions/) ([PR #8603](https://github.com/invertase/react-native-firebase/pull/8603)).
1313
1414
Ephemeral tracker; see [OKF policy](../documentation-policy.md).
@@ -88,6 +88,8 @@ Follow-on **after** Phases 0–5 and coordinated break. Not in scope unless test
8888

8989
**Deferral discriminator:** if **area-focused** e2e or device testing shows TurboModule migration **cannot** work with the legacy event proxy, escalate that package's event path into the active migration PR — do not wait for Phase C.
9090

91+
**Messaging event-delivery e2e (Phase C — investigate before enabling):** `packages/messaging/e2e/messaging.e2e.js` foreground `onMessage()` delivery test stays **`xit`** (disabled since modular e2e consolidation, 2023). Un-skipping without a harness audit risks intermittent Android failures (`TestsAPI.messaging().sendToDevice` / FCM data payload; see [#8736](https://github.com/invertase/react-native-firebase/issues/8736), [#8670](https://github.com/invertase/react-native-firebase/issues/8670), [#8392](https://github.com/invertase/react-native-firebase/issues/8392)). Phase C must define stable pass criteria (device vs emulator, data-only vs notification+data, delegation settings) and broader event coverage (token refresh, notification-opened, iOS background timing vs `signalBackgroundMessageHandlerSet`) before re-enabling as a CI gate — per [NewArch-AD-4 § messaging testing requirement](architecture-decisions.md#messaging--defer-eventemitter-cutover-migrate-shell-only-in-phase-4).
92+
9193
---
9294

9395
## Package inventory
@@ -324,7 +326,7 @@ Skip steps 1–2 when spec shape is known (most Tier D packages).
324326

325327
**Next item:** Phase **4** `messaging` TurboModule migration
326328

327-
**Current gates:** P4m `implementation` — shell only per [ADR-4 § messaging](architecture-decisions.md#messaging--defer-eventemitter-cutover-migrate-shell-only-in-phase-4).
329+
**Current gates:** P4m committed. **Next:** Phase 4 `database` (or next Tier A/B item).
328330

329331
**Host rule:** one `:test-cover` at a time — never parallel subagents with e2e.
330332

@@ -354,7 +356,7 @@ Skip steps 1–2 when spec shape is known (most Tier D packages).
354356
| Phase S0 compare-types registration | S0 | **closed** | **closed** | **closed** | done | `none` (`compare:types`) | `chore(compare-types): register remaining migrated packages` | Committed 2026-07-01. 8 pkgs registered; compare:types 19/19 green. |
355357
| Phase 3.5 guardrails | P3.5 | **closed** | **closed** | **closed** | done | `full` | `fix: add codegen verify and spec-native parity tests` | Committed `64f99c53d` 2026-07-02. |
356358
| Phase 4a messaging event decision | P4a | n/a | n/a | n/a | done | `none` | none | **Defer** events to Phase C — [NewArch-AD-4 § messaging](architecture-decisions.md#messaging--defer-eventemitter-cutover-migrate-shell-only-in-phase-4). |
357-
| Phase 4 `messaging` TurboModules | P4m | **open** | open | open | **implementation** | `area-focused` | `feat(messaging)!: migrate messaging to TurboModules` | Shell only (P4a); area-focused e2e per ADR-4 testing requirement. |
359+
| Phase 4 `messaging` TurboModules | P4m | **closed** | **closed** | **closed** | done | `area-focused` | `feat(messaging)!: migrate messaging to TurboModules` | Turbo shell only (AD-4 event path preserved). Jest 38/38 + parity 32/32; codegen:verify incl messaging; legacy Java removed. iOS/Android area e2e green on method-call + listener-registration scope. Foreground `onMessage` delivery test **left `xit`** — flaky FCM harness; re-enable in [Phase C](#deferred-cleanup-phase-eventemitter). |
358360
| Phase Docs — NA reqs + consolidation | PDoc | open | open | open | documentation | `none` | `docs: new-architecture requirements + migration consolidation` | Opportunistic; does not gate 4. |
359361
| Phase PD — platform divergence | PPD | open | open | open | documentation | `none` | `docs(<pkg>): …` per package | Opportunistic; gap-analysis first. |
360362
| Phase R — remove `NativeModules` fallback | PR-fallback | open | open | open | pre-merge-validation | `full` | (Phase R) | Decision B. Jest + `app` e2e "unknown module throws". |

packages/app/__tests__/specNativeParityHelper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const MIGRATED_TURBO_PACKAGES = [
2121
'crashlytics',
2222
'storage',
2323
'functions',
24+
'messaging',
2425
] as const;
2526

2627
export type MigratedTurboPackage = (typeof MIGRATED_TURBO_PACKAGES)[number];

packages/messaging/RNFBMessaging.podspec

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ if coreVersionDetected != coreVersionRequired
1010
end
1111
firebase_ios_target = appPackage['sdkVersions']['ios']['iosTarget']
1212
firebase_macos_target = appPackage['sdkVersions']['ios']['macosTarget']
13+
firebase_tvos_target = appPackage['sdkVersions']['ios']['tvosTarget']
1314

1415
Pod::Spec.new do |s|
1516
s.name = "RNFBMessaging"
@@ -25,15 +26,16 @@ Pod::Spec.new do |s|
2526
s.social_media_url = 'http://twitter.com/invertaseio'
2627
s.ios.deployment_target = firebase_ios_target
2728
s.macos.deployment_target = firebase_macos_target
28-
s.source_files = 'ios/**/*.{h,m}'
29+
s.tvos.deployment_target = firebase_tvos_target
30+
s.source_files = 'ios/**/*.{h,m,mm,cpp,swift}'
31+
s.exclude_files = 'ios/generated/RCTThirdPartyComponentsProvider.*', 'ios/generated/RCTAppDependencyProvider.*', 'ios/generated/RCTModuleProviders.*', 'ios/generated/RCTModulesConformingToProtocolsProvider.*', 'ios/generated/RCTUnstableModulesRequiringMainQueueSetupProvider.*'
2932

3033
s.dependency 'RNFBApp'
3134

32-
# React Native dependencies
33-
if defined?(install_modules_dependencies()) != nil
34-
install_modules_dependencies(s);
35-
else
36-
s.dependency "React-Core"
35+
install_modules_dependencies(s);
36+
37+
if defined?(ENV["RCT_NEW_ARCH_ENABLED"]) != nil && (ENV["RCT_NEW_ARCH_ENABLED"] == '0')
38+
raise "#{s.name} requires New Architecture. Enable New Architecture to use this module"
3739
end
3840

3941
if defined?($FirebaseSDKVersion)

packages/messaging/android/build.gradle

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ android {
130130
targetCompatibility = JavaVersion.VERSION_11
131131
}
132132
}
133+
134+
sourceSets {
135+
main {
136+
java.excludes = ['**/generated/jni/**']
137+
}
138+
}
133139
}
134140

135141
repositories {
@@ -147,3 +153,21 @@ ReactNative.shared.applyPackageVersion()
147153
ReactNative.shared.applyDefaultExcludes()
148154
ReactNative.module.applyAndroidVersions()
149155
ReactNative.module.applyReactNativeDependency("api")
156+
157+
def isNewArchitectureDisabled() {
158+
return project.hasProperty("newArchEnabled") && project.newArchEnabled != "true"
159+
}
160+
161+
if (isNewArchitectureDisabled()) {
162+
def ANSI_RED = "\u001B[31m";
163+
def ANSI_RESET = "\u001B[0m";
164+
165+
println("\n\n\n")
166+
println(ANSI_RED + "**************************************************************************************************************")
167+
println("\n\n\n")
168+
println("New Architecture support is required for @react-native-firebase/messaging")
169+
println("\n\n\n")
170+
println("**************************************************************************************************************" + ANSI_RESET)
171+
println("\n\n\n")
172+
System.exit(1)
173+
}

0 commit comments

Comments
 (0)