fix(analytics, messaging): dedupe GoogleUtilities classes under SPM dynamic frameworks - #9327
Conversation
0dc6c5a to
9a052ab
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9327 +/- ##
============================================
- Coverage 69.81% 69.81% -0.00%
Complexity 2129 2129
============================================
Files 439 439
Lines 25518 25518
Branches 4257 4257
============================================
- Hits 17814 17813 -1
Misses 6360 6360
- Partials 1344 1345 +1
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
9a052ab to
a109c68
Compare
mikehardy
left a comment
There was a problem hiding this comment.
In the work queue for Swift / Kotlin port I've got a queue item for ccache / build optimization with some gap analysis already - goal of that queue item is to seed a new queue specifically looking at how ccache fits (or doesn't!) in a Swift-centric world along with some specific probes and things to test from gap analysis. It's persisted in the related linear area
This specific fix looks fine - I think it will conflict a little but not in any difficult to resolve way and only in the test-expo / dSYMs upload area, not the core fix which appears to be +1, so, +1
| if [[ ${PODS_ROOT} && -f "${PODS_ROOT}/FirebaseCrashlytics/run" ]]; then | ||
| echo "info: Exec FirebaseCrashlytics Run from Pods" | ||
| "${PODS_ROOT}/FirebaseCrashlytics/run" | ||
| if ! "${PODS_ROOT}/FirebaseCrashlytics/run"; then |
There was a problem hiding this comment.
didn't the test-expo PR duplicate / touch this area as well?
| }, | ||
| "plugins": [ | ||
| "@react-native-firebase/app", | ||
| "@react-native-firebase/analytics", |
There was a problem hiding this comment.
I think will collide with test-expo separate PR as well
…ynamic frameworks Under Expo's SPM + dynamic frameworks path, RNFBAnalytics.podspec and RNFBMessaging.podspec only declared their direct Firebase SPM products (FirebaseAnalytics, FirebaseMessaging). GULNetwork, GULReachability, and GULMethodSwizzler were only reachable transitively through those, and Xcode's SPM integration doesn't reliably promote a transitively-only reached product to a shared PackageProduct.framework. It compiled a private copy of each straight into RNFBAnalytics.framework and RNFBMessaging.framework instead, so GULNetwork/GULMutableDictionary/ GULNetworkURLSession/GULReachabilityChecker/GULSessionDeallocTracker/ GULSwizzler ended up defined twice at runtime once both pods were loaded. The fix declares those same GoogleUtilities products as an explicit top-level spm_dependency on both podspecs, the same way GULAppDelegateSwizzler already was. That's enough for Xcode to treat them as shared and build one dynamic framework per product instead of duplicating them. test-expo-ios-link.sh gained a new #9322 check that walks every built .framework, nm's its defined external symbols, and fails if any of the affected GUL classes are defined in more than one framework or privately inside any RNFB* framework rather than a shared PackageProduct. Also removed the hardcoded CC=clang/LD=clang xcodebuild args, which broke pure-Swift SPM targets pulled in by this repro (FirebaseCoreInternal). test-expo's package list and the closer's RNFB* target discovery are already on main, so this commit does not touch them. The #9322 check still runs against that graph. Every watched GUL class, including the two SWIFTPM_MODULE_BUNDLER_FINDER symbols, has exactly one owner, and that owner is never an RNFB* framework. Added a short OKF note in ios-spm-native-imports.md: explicit GoogleUtilities products can share a PackageProduct. That does not extend to FirebaseCore/ FIRApp.
a109c68 to
214ebf9
Compare
RNFBAnalytics.podspecandRNFBMessaging.podspeconly declared their direct Firebase SPM product (FirebaseAnalytics,FirebaseMessaging). Before this fix,GULNetwork,GULReachability, andGULMethodSwizzlerended up compiled privately into bothRNFBAnalytics.frameworkandRNFBMessaging.frameworkinstead of a single shared dynamic framework, so severalGUL*classes were defined twice at runtime once both pods were loaded together.Confirmed: the raw
FirebaseAnalytics/GoogleAppMeasurement.xcframeworkbinaries don't bake in copies of these classes (checked withnm), they only reference them as undefined externals like any source-based product.Not fully confirmed: why Xcode's SPM integration fails to promote this specific cluster to a shared framework once Analytics is in the graph, when an equivalent pure-source pairing (Messaging + Auth, sharing the
GULAppDelegateSwizzlercluster instead) promotes cleanly with zero private copies. It isn't simply "transitive vs explicit" either: firebase-ios-sdk's ownPackage.swiftalready listsGULNetwork/GULMethodSwizzleras explicit top-level products onFirebaseAnalyticsWrapper, in the same array asGULAppDelegateSwizzler, and only the latter shared correctly. Whether Analytics's target also carrying a binaryTarget dependency changes Xcode's per-target sharing heuristic is a live, untested hypothesis.What's confirmed to fix it: declaring these same GoogleUtilities products as an explicit top-level
spm_dependencydirectly on both podspecs makes Xcode build one shared dynamic framework per product and link bothRNFBAnalyticsandRNFBMessagingagainst it.test-expois widened to the same first-party package list as #9313 (addsai,app-check,auth,crashlytics,database,firestore,functions,installations,perf,phone-number-verification,remote-config,storage), so the closer proves this against the real graph new packages land in, not just Analytics/Messaging in isolation.test-expo-ios-link.shnow discovers everyRNFB*target dynamically instead of hardcoding Analytics/Messaging,nm's each built.framework, and fails if any watchedGUL*class (including the twoSWIFTPM_MODULE_BUNDLER_FINDERfinder symbols) is missing, defined in more than one framework, or defined privately inside anyRNFB*framework rather than a shared package product. Verified clean across the full 13-target graph: nospm_dependencyneeded anywhere beyond Analytics/Messaging.Also dropped the hardcoded
CC=clang/LD=clangxcodebuild args in that script. They're a ccache-pairing leftover never actually wired to this workflow, and they broke the pure-Swift SPM target (FirebaseCoreInternal) this repro pulls in once the graph widens.Adding Crashlytics to
test-exposurfaced two unrelated pre-existing bugs inpackages/crashlytics/ios_config.shthat blocked the closer from reaching its own diagnosis: the dSYM plist lookup for Expo's target-folder layout (already fixed onmain, just needed a rebase here) andupload-symbolshaving no non-fatal handling, so any upload failure hard-failed the wholexcodebuildinstead of warning. The non-fatal-upload fix is tracked and PR'd standalone in #9333; it's duplicated here too since this closer needs it in the meantime, and it'll collapse to a no-op on the next rebase once #9333 merges.Short OKF note added in
okf-bundle/ios-spm-native-imports.md: explicit GoogleUtilities products can share a PackageProduct; that doesn't extend to FirebaseCore/FIRApp.CI note:
Create patch-package Patcheshas been flaky on this PR (unrelated to the change). The nestedyarn addcalls inside itsCreate Patchesstep aren't wrapped in the retry action the rootYarn Installstep uses, so a registry hiccup or runner comms timeout fails the whole job. A re-run on the same commit has passed each time so far.