feat!: migrate to TurboModules#9080
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request initiates the coordinated migration of the React Native Firebase monorepo to the React Native New Architecture using TurboModules. It establishes the foundational infrastructure, including a unified module resolver, committed generated artifacts, and updated type-testing configurations. The changes focus on migrating the core Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request removes the deprecated namespaced JavaScript API across multiple packages (such as analytics, app-check, and app-distribution) to transition to a modular-only architecture for v26. It also introduces the foundational TurboModule migration for the app package under the New Architecture, updating native iOS/Android configurations, JNI codegen, and test harness setups. Feedback on these changes highlights several stability improvements, including preventing a TypeError in initializeAppCheck when options are omitted, ensuring correct context and null-safety when resolving native module constants, adding try-catch protection to app-destroy callbacks, and guarding against potential null pointer exceptions when querying external storage directories in Android.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| return new ReactNativeFirebaseAppCheckProvider(); | ||
| } | ||
|
|
||
| initializeAppCheck(options: AppCheckOptions): Promise<void> { |
There was a problem hiding this comment.
If options is not provided (which is allowed by the modular initializeAppCheck signature), calling options.isTokenAutoRefreshEnabled will throw a TypeError: Cannot read properties of undefined. Defaulting options to an empty object prevents this crash and allows the subsequent hasProviderOptions check to throw a clean, descriptive error instead.
| initializeAppCheck(options: AppCheckOptions): Promise<void> { | |
| initializeAppCheck(options: AppCheckOptions = {} as AppCheckOptions): Promise<void> { |
| let constants = memoizedModuleConstants.get(moduleName); | ||
| if (!constants) { | ||
| constants = (getConstants as () => Record<string, unknown>)(); | ||
| memoizedModuleConstants.set(moduleName, constants); | ||
| } |
There was a problem hiding this comment.
Calling getConstants without a this context can lead to unexpected behavior or errors if the underlying JSI/native implementation expects the host object context. Using getConstants.call(module) ensures the correct context is preserved. Additionally, guarding against a null or undefined return value from getConstants() prevents a TypeError when calling Object.keys(constants) later.
| let constants = memoizedModuleConstants.get(moduleName); | |
| if (!constants) { | |
| constants = (getConstants as () => Record<string, unknown>)(); | |
| memoizedModuleConstants.set(moduleName, constants); | |
| } | |
| let constants = memoizedModuleConstants.get(moduleName); | |
| if (!constants) { | |
| constants = getConstants.call(module) || {}; | |
| memoizedModuleConstants.set(moduleName, constants); | |
| } |
| for (let i = 0; i < onAppDestroyCallbacks.length; i++) { | ||
| onAppDestroyCallbacks[i]?.(app); | ||
| } |
There was a problem hiding this comment.
If any of the onAppDestroyCallbacks throws an error, the loop will be interrupted, preventing subsequent callbacks from executing and leaving the app registry in an inconsistent state. Wrapping each callback execution in a try...catch block ensures robust cleanup.
for (let i = 0; i < onAppDestroyCallbacks.length; i++) {
try {
onAppDestroyCallbacks[i]?.(app);
} catch (e) {
console.error('Error during onAppDestroy callback:', e);
}
}| constants.put( | ||
| KEY_PICS_DIRECTORY, | ||
| Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) | ||
| .getAbsolutePath()); | ||
|
|
||
| constants.put( | ||
| KEY_MOVIES_DIRECTORY, | ||
| Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES) | ||
| .getAbsolutePath()); |
There was a problem hiding this comment.
Environment.getExternalStoragePublicDirectory can return null if the external storage is unmounted or unavailable. Calling .getAbsolutePath() directly on the returned value without a null check poses a NullPointerException risk. Guarding these calls ensures defensive stability.
| constants.put( | |
| KEY_PICS_DIRECTORY, | |
| Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) | |
| .getAbsolutePath()); | |
| constants.put( | |
| KEY_MOVIES_DIRECTORY, | |
| Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES) | |
| .getAbsolutePath()); | |
| File picsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); | |
| constants.put(KEY_PICS_DIRECTORY, picsDir != null ? picsDir.getAbsolutePath() : ""); | |
| File moviesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES); | |
| constants.put(KEY_MOVIES_DIRECTORY, moviesDir != null ? moviesDir.getAbsolutePath() : ""); |
Add TurboModule migration OKF bundle with work queue, implementation workflow extending change authoring, and package-extensions cross-link.
…n infra BREAKING CHANGE: App/Core modules native bridge requires New Architecture. Migrate RNFBAppModule and RNFBUtilsModule to Codegen TurboModules with unified resolver, lazy Proxy wrapper, and committed generated artifacts. Includes functions codegenConfig rename (NewArch-AD-7), test harness overrides, architecture decisions, and validation workflow hardening.
Register @react-native-firebase/app in compare:types and document 25 modular API deltas against firebase-js-sdk. Type SDK_VERSION as string.
BREAKING CHANGE: Firestore native bridge requires New Architecture.
Legacy NativeModules bridge removed; four Codegen TurboModule specs
(NativeRNFBTurboFirestore{,Collection,Document,Transaction}) with
committed generated artifacts, Android/iOS turbo shells, and JS wiring.
Environment.getExternalStoragePublicDirectory can return null when external storage is unavailable; guard the pictures and movies directory constants instead of dereferencing getAbsolutePath directly.
d38edde to
6924759
Compare
Summary
Coordinated New Architecture migration: legacy
NativeModules→ Codegen TurboModules (NativeRNFBTurbo*), unified module resolver, committed generated artifacts, and shared test/validation infrastructure.Land so far:
app(foundation + utils) andfunctions(reference). Remaining native packages follow the same per-package loop before the coordinated semver major.Infra in this branch: architecture decisions (OKF), gitignored harness overrides, implementation/review gate hardening, Jest TurboModule contract test,
compare:typesconfig forapp.Related #8584
Migration checklist
@react-native-firebase/app@react-native-firebase/functions@react-native-firebase/auth@react-native-firebase/firestore@react-native-firebase/database@react-native-firebase/messaging@react-native-firebase/storage@react-native-firebase/crashlytics@react-native-firebase/analytics@react-native-firebase/remote-config@react-native-firebase/app-check@react-native-firebase/perf@react-native-firebase/installations@react-native-firebase/in-app-messaging@react-native-firebase/app-distribution@react-native-firebase/ml@react-native-firebase/phone-number-verificationTest plan