Skip to content

fix(ios): resolve duplicate GUL/Firebase classes under Expo SPM dynamic frameworks - #9326

Draft
russellwheatley wants to merge 1 commit into
mainfrom
feature/cprn-465-umbrella-swift-facade-spike
Draft

russellwheatley wants to merge 1 commit into
mainfrom
feature/cprn-465-umbrella-swift-facade-spike

Conversation

@russellwheatley

@russellwheatley russellwheatley commented Sep 18, 2026

Copy link
Copy Markdown
Member

Demonstrates a fix for duplicate GUL*/Firebase classes compiled into both RNFBAnalytics.framework and RNFBMessaging.framework under Expo's SPM + useFrameworks: dynamic path (#9322), using the local dynamic umbrella a firebase-ios-sdk maintainer suggested (since firebase-ios-sdk itself can't ship one, firebase/firebase-ios-sdk#8945).

Once Firebase goes through the umbrella, the individual Firebase headers are hidden from Analytics's and Messaging's .mm/.m files, so their __has_include guards fall back to @import FirebaseAnalytics/@import FirebaseMessaging, illegal in Objective-C++ without -fcxx-modules (which in turn breaks React Native's own build).

To get a clean build under the umbrella:

  • Move every direct Firebase Analytics/Messaging call out of .mm/.m and behind a small Swift facade (RNFBAnalyticsFacade, RNFBMessagingFacade), the same pattern RNFBFunctionsCallHandler already used.
  • Rewrite the FCM token-refresh delegate in Swift, split into an internal implementation (conforms to Firebase's MessagingDelegate) and a thin public wrapper, since a public Swift type conforming to a hidden framework's protocol forces Swift to add its own illegal @import to the generated header.
  • Add a same-pod Objective-C bridge (RNFBMessagingEventEmitter) so the Swift delegate can still reach RNFBApp's RNFBRCTEventEmitter, since Swift can't import a sibling RNFB pod directly (every RNFB pod marks nearly all its headers private).
  • Give RNFBAnalytics an explicit framework search path and -framework GoogleAppMeasurement: FirebaseAnalytics resolves to a prebuilt XCFramework rather than plain source, so it doesn't link automatically under the umbrella.

yarn test-expo:ios:link now builds clean, and a new assert_dynamic_umbrella_graph check confirms none of RNFBApp/RNFBAnalytics/RNFBMessaging define their own FIRApp/GUL* symbols, and all three link the shared dynamic RNFBFirebase umbrella rather than embedding their own copy.

This is a draft for discussion, not ready to merge: RNFBApp has its own separate fix and isn't reworked here, and this only covers the Expo/SPM + dynamic frameworks path.

…ic frameworks

Demonstrates a fix for duplicate GUL*/Firebase classes compiled into both
RNFBAnalytics.framework and RNFBMessaging.framework under Expo's SPM +
useFrameworks: dynamic path (#9322), using the local dynamic umbrella a
firebase-ios-sdk maintainer suggested (since firebase-ios-sdk itself can't
ship one, firebase/firebase-ios-sdk#8945).

Once Firebase goes through the umbrella, the individual Firebase headers are
hidden from Analytics's and Messaging's .mm/.m files, so their __has_include
guards fall back to @import FirebaseAnalytics/@import FirebaseMessaging,
illegal in Objective-C++ without -fcxx-modules (which in turn breaks React
Native's own build).

To get a clean build under the umbrella:
- Move every direct Firebase Analytics/Messaging call out of .mm/.m and
  behind a small Swift facade (RNFBAnalyticsFacade, RNFBMessagingFacade),
  the same pattern RNFBFunctionsCallHandler already used.
- Rewrite the FCM token-refresh delegate in Swift, split into an internal
  implementation (conforms to Firebase's MessagingDelegate) and a thin
  public wrapper, since a public Swift type conforming to a hidden
  framework's protocol forces Swift to add its own illegal @import to the
  generated header.
- Add a same-pod Objective-C bridge (RNFBMessagingEventEmitter) so the
  Swift delegate can still reach RNFBApp's RNFBRCTEventEmitter, since Swift
  can't import a sibling RNFB pod directly (every RNFB pod marks nearly all
  its headers private).
- Give RNFBAnalytics an explicit framework search path and -framework
  GoogleAppMeasurement: FirebaseAnalytics resolves to a prebuilt XCFramework
  rather than plain source, so it doesn't link automatically under the
  umbrella.

yarn test-expo:ios:link now builds clean, and a new
assert_dynamic_umbrella_graph check confirms none of
RNFBApp/RNFBAnalytics/RNFBMessaging define their own FIRApp/GUL* symbols,
and all three link the shared dynamic RNFBFirebase umbrella rather than
embedding their own copy.

This is a draft for discussion, not ready to merge: RNFBApp has its own
separate fix and isn't reworked here, and this only covers the Expo/SPM +
dynamic frameworks path, not CocoaPods-only installs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant