feat(ios): add SPM dependency resolution support alongside CocoaPods#8933
feat(ios): add SPM dependency resolution support alongside CocoaPods#8933jsnavarroc wants to merge 40 commits into
Conversation
|
Johan Navarro seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
|
@jsnavarroc is attempting to deploy a commit to the Invertase Team on Vercel. A member of the Team first needs to authorize it. |
Add dual SPM/CocoaPods dependency resolution for Firebase iOS SDK. When React Native >= 0.75 is detected, Firebase dependencies are resolved via Swift Package Manager (spm_dependency). For older versions or when explicitly disabled ($RNFirebaseDisableSPM = true), CocoaPods is used. Changes: - Add firebase_spm.rb helper with firebase_dependency() function - Add firebaseSpmUrl to packages/app/package.json (single source of truth) - Update all 16 podspecs to use firebase_dependency() - Add #if __has_include guards in 43 native iOS files for dual imports - Add CI matrix (spm × cocoapods × debug × release) in E2E workflow - Add Ruby unit tests for firebase_spm.rb - Add documentation at docs/ios-spm.md
…dSupport is true When $RNFirebaseAnalyticsWithoutAdIdSupport = true with SPM enabled, FirebaseAnalytics pulls in GoogleAppMeasurement which contains APMETaskManager and APMMeasurement cross-references. These cause linker errors when FirebasePerformance is not installed. Switch to FirebaseAnalyticsCore (-> GoogleAppMeasurementCore) in that case, which has no IDFA and no APM symbols. CocoaPods path is unchanged. docs: add Section 8 with 5 real integration bugs found during tvOS Xcode 26 migration and their solutions
fix(analytics): use FirebaseAnalyticsCore when WithoutAdIdSupport + SPM
Additional fix included:
|
|
Hey @mikehardy — could you approve the workflows to run on this PR when you get a chance? The CI checks are blocked waiting for maintainer approval. Happy to address any feedback once they run. Thanks! |
mikehardy
left a comment
There was a problem hiding this comment.
Wow! This is pretty amazing. Thank you for proposing this - just finished first pass review and while I left comments all over the place I hope none of that gives the feeling that this isn't amazing, and that we won't merge SPM support, it is something this repository obviously needs. So again, thank you
But then of course there are all the comments with some specific questions, some pings out to a firebase-ios-sdk maintainer (hi Paul 👋 ) that I collaborate with on occasion, and some notes to myself regarding testing
| #import <Firebase/Firebase.h> | ||
| #else | ||
| @import FirebaseCore; | ||
| @import FirebaseAnalytics; |
There was a problem hiding this comment.
Taking note of your comment here on the FirebasePerformance symbols missing at link time if ad ids are disabled does this FirebaseAnalytics import still work here in the FirebaseAnalyticsCore dependency case? #8933 (comment)
There was a problem hiding this comment.
Good question. Yes, @import FirebaseAnalytics still works when the dependency is FirebaseAnalyticsCore. They share the same Clang module and headers, the difference is only at link time: FirebaseAnalyticsCore links GoogleAppMeasurementCore (no IDFA, no APM symbols) while FirebaseAnalytics links GoogleAppMeasurement (includes APMETaskManager, APMMeasurement). The import resolves the same API surface in both cases.
| s.frameworks = 'AdSupport' | ||
| end | ||
|
|
||
| # GoogleAdsOnDeviceConversion (CocoaPods only, not available in firebase-ios-sdk SPM) |
There was a problem hiding this comment.
Is there some documentation on how to access on-device conversion in the SPM case? This is surprising to me as I thought on-device conversion was one of the newer features of firebase-ios-sdk which creates the expectation in me that it should be available there somehow ? Perhaps just built in to core SPM dep I'm not sure
There was a problem hiding this comment.
GoogleAdsOnDeviceConversion is a static xcframework distributed separately from firebase-ios-sdk. It is NOT available as an SPM product in Package.swift. When using SPM (dynamic linkage), it causes duplicate symbol errors.
In the latest commit I've added:
- A clear comment explaining the limitation
- A runtime warning when the user enables it in SPM mode
- Documentation pointing users to set
$RNFirebaseDisableSPM = trueif they need on-device conversion.
If firebase-ios-sdk adds it as an SPM product in the future, we can remove this restriction.
There was a problem hiding this comment.
This is something we need to figure out - it must be possible as I'm seeing reports of regular (native) integrations of firebase-ios-sdk as well as Flutter folks using ODC / On Device Conversion) - sometimes with some difficult for example firebase/firebase-ios-sdk#15916 but with eventual success
There was a problem hiding this comment.
GoogleAdsOnDeviceConversion isn't part of firebase-ios-sdk's own Package.swift (checked directly, no references at all), but Google publishes it as its own standalone SPM package: googleads/google-ads-on-device-conversion-ios-sdk. It has its own Package.swift with a GoogleAdsOnDeviceConversion library product wrapping a prebuilt xcframework binary target.
RN's SPMManager (node_modules/react-native/scripts/cocoapods/spm.rb) keys dependencies by pod target and package URL in a list, so a second, independent spm_dependency() call on the same pod target resolves an unrelated SPM package just fine which is the supported pattern.
Fixed in 1c2fee7 - added a second spm_dependency() call for this package when the SPM path is active, replacing the warn-and-refuse behavior. Noted the -ObjC/-lc++ linker flag caveat from firebase-ios-sdk#15916 in the comment in case anyone hits it (that report was about an indirect/wrapper package scenario, not our direct call, but worth flagging for troubleshooting).
| #if __has_include(<Firebase/Firebase.h>) | ||
| #import <Firebase/Firebase.h> | ||
| #import <FirebaseAppCheck/FIRAppCheck.h> | ||
| #elif __has_include(<FirebaseAppCheck/FirebaseAppCheck.h>) | ||
| #import <FirebaseAppCheck/FirebaseAppCheck.h> |
There was a problem hiding this comment.
<Firebase/Firebase.h> will always be found in the CocoaPods case won't it? It's the most fundamental header if I understand correctly. Does it transitively include <FirebaseAppCheck/FIRAppCheck.h> such that the explicit import is no longer required then (or maybe was never required?). Surprising this compiles as I think that preprocessor branch is likely the only __has_include branch that will ever be taken, and it makes me think the #elif __has_include(<FirebaseAppCheck/FirebaseAppCheck.h>) branch may not even be needed, I can't see how <Firebase/Firebase.h> won't be found
There was a problem hiding this comment.
You're right that <Firebase/Firebase.h> will always be found in CocoaPods since it's the umbrella header. The #elif branch covers an edge case where someone installs only individual Firebase pods without the umbrella Firebase/Firebase pod. In practice, since RNFB always depends on RNFBApp which brings FirebaseCore, the first branch is effectively always taken in CocoaPods.
The three-branch structure is: CocoaPods umbrella, then CocoaPods individual pods, then SPM (@import). I can simplify to just #if/#else (CocoaPods umbrella vs SPM @import) if you prefer, let me know.
There was a problem hiding this comment.
@mikehardy, for direct firebase-ios-sdk usage, the recommendation is to use the individual pods (pod 'FirebaseAppCheck' (no /)) rather than the umbrella pod (pod 'Firebase/AppCheck'). In any case, the new versions of Firebase.podspec will continue to be released with the other pods up through October 2026.
|
Hey @jsnavarroc 👋 just a gentle ping on this - are you interested in / have time to work on moving this forward? Happy to collaborate, and it's a pretty important feature so I'm very interested personally. Curious for your thoughts |
|
Hey @mikehardy! Yes, absolutely, I'm actively working on this and just pushed a new commit addressing all the review comments. Sorry for the delay. Changes in the latest commit
AskIf you or anyone on the team has bandwidth to test this branch on different devices/platforms (iOS, tvOS, macCatalyst) and with different configurations (SPM vs CocoaPods, dynamic vs static), that would be incredibly valuable. I've verified it on iOS 26 simulator and tvOS 26 (Apple TV) but broader coverage would help catch edge cases before merge. I'll reply to each individual review comment below with details. |
FirebaseInstallations is a transitive dependency of FirebaseCore. With SPM dynamic linking, transitive frameworks are not embedded automatically — they must be declared explicitly as SPM products. Without this, dyld crashes at launch with: 'symbol not found in flat namespace _FIRInstallationIDDidChangeNotification'
… improve SPM comments
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 496d97c. Configure here.
|
Great to see the progress in this PR! I'm cc'ing @ncooke3, who is now a better contact than me for the firebase-ios-sdk questions in the comments. |
Heads up: tvOS + Xcode 26 hybrid SPM/CocoaPods edge case worth documentingWhile integrating this PR in a tvOS React Native project (hybrid CocoaPods + SPM resolution, Xcode 26, RN 0.77 with Symptom
No formal crash log is generated, Root causeThe combination that triggers it:
The Release + TestFlight pipeline strips Firebase symbols from the main binary's dynamic symbol table because the static analyzer sees them as "unused", nothing in the binary statically references them, RNFB frameworks resolve them at runtime via flat-namespace dynamic lookup. When dyld tries to resolve them on device after TestFlight processing, they're gone. Local Xcode installs don't exhibit this because development builds don't apply the same aggressive stripping. Diagnostic evidenceBefore fix (binary that crashed in TestFlight):
After fix (binary that works in TestFlight):
WorkaroundAnti-stripping settings on the main app target's Release configuration + exporting static symbols to the dynamic symbol table, added via main_target.build_configurations.each do |cfg|
next unless cfg.name == 'Release'
cfg.build_settings['STRIP_SWIFT_SYMBOLS'] = 'NO'
cfg.build_settings['DEAD_CODE_STRIPPING'] = 'NO'
cfg.build_settings['STRIP_INSTALLED_PRODUCT'] = 'NO'
cfg.build_settings['COPY_PHASE_STRIP'] = 'NO'
cfg.build_settings['DEPLOYMENT_POSTPROCESSING'] = 'NO'
ldflags = Array(cfg.build_settings['OTHER_LDFLAGS'] || ['$(inherited)'])
ldflags << '-Wl,-export_dynamic' unless ldflags.include?('-Wl,-export_dynamic')
cfg.build_settings['OTHER_LDFLAGS'] = ldflags
endThis belongs in the integrator's Podfile, not in the library, because it's specific to the hybrid linkage strategy and tvOS, and would be intrusive for iOS projects that don't need it (binary size doubles, Release optimizations disabled). SuggestionSince this is very hard to diagnose without the crashlog (which doesn't get generated) and only manifests after TestFlight upload (long iteration cycle), it might be worth considering one of:
Happy to open a separate PR with either approach if maintainers think it's worth it, fully understand if you'd rather keep this PR focused and tackle it separately later. Flagging it here so it doesn't get lost. Context: reproducible on my setup (Apple TV 4K, tvOS 26, Xcode 26.3, cc @ncooke3 (per @paulb777's earlier comment pointing to you for firebase-ios-sdk questions), sharing in case this stripping interaction is relevant on the Firebase SDK side. |
|
Hi @jsnavarroc, nice testing & investigation! If you didn't consider it, adding the In any case, I'll defer to @mikehardy here, but your suggestions sound reasonable. |
Add an RNFB SPM post-install patch that injects an app-target build phase for Firebase SPM dynamic frameworks. The phase copies frameworks built under Xcode's PackageFrameworks directory into the app Frameworks folder when they were not already embedded by CocoaPods. This fixes release launches where RNFBStorage links FirebaseStorage, FirebaseStorage links FirebaseAppCheckInterop, but the app bundle does not contain FirebaseAppCheckInterop.framework. The fix keeps Storage declaring only the public FirebaseStorage product and avoids requiring consumers to install App Check or add a Podfile workaround. Also regenerates the iOS test project so the new embed phase is present.
|
I pushed a follow-up fix for the Release+SPM launch failure here: What was happening:
Instead, |
mikehardy
left a comment
There was a problem hiding this comment.
still reading through this - got to the Obj-C helper stuff - that's a pretty big change and I still need to consider it. I wonder if converting to swift internally here in react-native-firebase would simplify things at all - I'm aware of the need for Objective-C in order to complete the journey to native from TurboModules codegen so I'm not sure it would be simplifying at all but I was casting about for any idea to avoid need for one more indirection step of "helper" files to keep our Obj-C "firebase-clean" in the SPM environment
| "iteration": iterationArray, | ||
| "buildmode": buildmode | ||
| "buildmode": buildmode, | ||
| "dep-resolution": depResolution |
There was a problem hiding this comment.
great use of the matrix capability
| s.frameworks = 'AdSupport' | ||
| end | ||
|
|
||
| # GoogleAdsOnDeviceConversion (CocoaPods only, not available in firebase-ios-sdk SPM) |
There was a problem hiding this comment.
This is something we need to figure out - it must be possible as I'm seeing reports of regular (native) integrations of firebase-ios-sdk as well as Flutter folks using ODC / On Device Conversion) - sometimes with some difficult for example firebase/firebase-ios-sdk#15916 but with eventual success
Replace the explicit rnfirebase_add_spm_embed_phase Podfile call with a CocoaPods-level hook (Pod::Installer#run_podfile_post_install_hooks) so Firebase's SPM-built dynamic frameworks are embedded automatically on every pod install, with no consumer Podfile edits and no dependency on React Native's private SPM internals.
|
Hi @russellwheatley 👋 — first, a genuine thank you for pushing the SPM work forward directly on my fork over the past week. The embed-frameworks fix ( I need to raise two concerns publicly on this PR — not just for our project, but because they materially affect any consumer waiting on SPM support to land in a shipping release. Would appreciate direct answers from you (and/or @mikehardy) so the community can plan accordingly. Concern 1: this PR is no longer scoped to "SPM support"The PR title says Since ~26-Jun, direct pushes to the head branch have bundled the SPM plumbing with commits that ARE valuable individually but come paired with breaking changes for anyone on the current stable API surface:
Direct question 1: why are these changes on this PR at all? They're not "SPM support" — they're a v26-shape refactor. Consumers reading a PR titled "SPM support" don't expect breaking API removal + TurboModules mandate inside it. Concern 2: why not defer TurboModules + namespaced-removal to a v26 release?SPM adoption should be orthogonal to three separate axes:
A consumer should be able to enable SPM without touching any of those three. That is what the original PR commits did ( There's real, immediate demand for SPM in this PR thread:
Direct question 2: why not ship SPM support as a Right now, an integrator who needs SPM has three options and all three are bad:
Reproducible evidence — same target, opposite outcomesI ran a side-by-side production-shape validation on tvOS 26.4 Simulator (Apple TV 4K 3rd gen), Xcode 26.6, Test A — Stable subset from
Result: Build SUCCEEDED, app runs, all pillars pass. Test B — Result: Build FAILED at Root cause: Even if we patch the consumer Podfile to expose those headers (feasible, ~4 lines), the JS API surface ( Where I've landed for nowI'm carrying a minimal patched branch internally that has just the SPM plumbing without the v26-shape refactors, plus the Proposed paths forwardThree options in order of increasing effort:
I'm happy to do the branch surgery to isolate the v25-safe SPM changes if that helps — you've already done the hard technical work; the split at this point is mostly Concrete next step I can take right now: if you want, I can open a companion PR ( Would that work for you? Happy to hop on a discussion if it's easier than PR comments. And again — thanks for pushing this. Really appreciate the direct collaboration on the fork. cc @mikehardy — flagging in case you want to weigh in on the release-line strategy. — Johan |
|
From a maintainer standpoint, it is not really important that SPM support is orthogonal, it is that as maintainers we have things we need to accomplish, and we process them as we can. In this case it just so happens that SPM support is the last of the 4 major transforms we had stacked up (don't forget converting to typescript!). Typescript, dropping namespace APIs, New Architecture support, and SPM. New Architecture landed, and v26 will be New Architecture only, it will include the TurboModules breaking changes - there are no supported versions of react-native that still run old architecture. v0.83 of react-native was the last react-native that supported old architecture, and it is older than the "current + 2 releases" support window for react-native. Dropping old architecture support shouldn't be controversial from a support standpoint. We're not going to devote any time to maintaining non-New Architecture code, including any branch maintenance work to land SPM support on old code here. That's not a good investment of scarce maintainer time. Dropping namespaced APIs landed - so v26 will be modular APIs only - the namespaced APIs have been deprecated for more than a year, across multiple breaking change releases in fact - which is quite a generous time frame to support them. Same as New Architecture - there is no reasonable argument to be made in favor of going back on those decisions, and we will not be dedicating any time on supporting old architecture or namespaced APIs, so none of your proposed 3 courses of actions makes sense as a time investment, in my opinion. Thus your assertion that this PR now "bundles" those changes is not really accurate in my opinion - it is more accurate to say this PR is targeted for a successful merge against current HEAD on main, period. That implies it necessarily needs to work with those two points above - new arch only, modular API only. It's not "bundling" anything. it is "merge-able", something kind of important for a PR... So, we've landed the rest, it's SPM's turn now. We'll likely release v26 prior to landing SPM support (likely in the next day or so even), but that's just a timing issue. SPM will land and we'll release it as soon as we can. But not based against old versions Moving forward - If you have specific technical feedback on the current shape of the PR vs hopes for old branch stuff that could really help though - please post a comment focused just on current technical problems with the PR as it is right now against current main and we can collaborate |
|
Thanks @mikehardy — appreciate the direct response. Quick clarification: I never asked for maintenance of the v25.x line. That wasn't the ask, and I understand the maintainer time argument. What I was actually trying to raise is different: the PR title is Not asking for anything to be undone — just noting that when SPM lands, it will effectively force the v26 migration as a side-effect, which wasn't obvious from the PR title. — Johan |
|
This PR, when merged, will be squashed into main. It won't "contain" the already landed breaking changes, it will just land on top of them. The PR only "contains" the other work in so far as main was merged into this branch to prove that it works on main, in 4d5b636 I wouldn't go as far as describing a PR that had main merged into it as "containing" main, per se - it's based on main now but that's different 🤔 |
…tor builds The existing iOS matrix only ever runs `xcodebuild build -sdk iphonesimulator`. Xcode's Archive action forces ONLY_ACTIVE_ARCH=NO and DEPLOYMENT_POSTPROCESSING=YES (full install-style stripping) for a real device SDK regardless of project/Podfile settings -- the actual difference between builds that work from Xcode/simulator and the tvOS TestFlight-only crash and release+SPM dyld launch failure already reported against this PR's SPM support. Add an `ios-release-archive` CI job (spm/cocoapods matrix, no Detox/emulator) that runs a real, unsigned `xcodebuild archive -destination 'generic/platform=iOS'` and a new verify-ios-release-archive.sh that walks every embedded binary's otool -L output and fails if any @rpath/*.framework dependency isn't actually embedded in Frameworks/. This automates, at real Archive-action fidelity, the check that was previously only done manually against a Release-iphonesimulator build when firebase_spm.rb's embed phase was fixed. Not a substitute for a signed TestFlight/device launch test -- it inspects the archived bundle statically and would not catch a pure runtime crash on its own, only the framework-embedding regression class.
Rename ios-spm.md to ios-spm.mdx to match the docs.page convention used by every other page, add the required frontmatter, and register it in docs.json — it was previously unreachable from the docs site sidebar. Also trims the stale Firebase SDK version header and fixes markdown table formatting/an MDX parsing error that surfaced once the file entered the `.mdx`-scoped lint:markdown check.
… date Lead with the CocoaPods trunk shutdown (read-only Dec 2, 2026, per the CocoaPods blog) as the motivating reason to adopt SPM, instead of the explicit-modules internals that aren't relevant to most readers.
The RNFBStorage/FIRStorage/StorageTask predicates were added to chase a specific deadlock that has since been found and fixed upstream. Removing them lightens the log stream query so CI runners settle faster.
GoogleAdsOnDeviceConversion isn't part of firebase-ios-sdk's own Package.swift, but Google publishes it as its own standalone SPM package (googleads/google-ads-on-device-conversion-ios-sdk) independent of the Firebase package graph. RN's SPMManager keys dependencies by pod target and package URL, so a second, independent spm_dependency() call resolves it alongside the existing FirebaseAnalytics dependency -- no need to force CocoaPods mode to use this feature anymore.
@mikehardy - not sure there is a way around this. Clang disables Moving to Swift isn't really an option. Right now, we're able to almost verbatim copy/paste Objective-C++ code in to Objective-C, if we converted it all to Swift it would be enormous, more risky and out of scope for this PR in my opinion and wouldn't help minimise diff of PR. The only thing that I'm not 100% sure, is whether @ncooke3 - could you confirm that last bit? |
That was my analysis as well, I suppose a better way of framing my hesitation is "I haven't found any way around this but it's a big enough change I just wanted one last look", which I did after making the comment -- and my findings were exactly what you found
This would be my last hope, an appeal to the expert in form of @ncooke3 or @paulb777 - not expecting a miracle but maybe a real ObjC++/Swift expert knows a trick 🤷 . Otherwise, I guess we go for it |
…hat to do as a consumer. okf-bundle has also been reduced to remove duplication and any overly verbose descriptions
Restore the pull request to its prior SPM and test-app configuration.
Fixes lint:ios:check indentation violations in method signature continuation lines across auth, crashlytics, database, firestore, installations, messaging, perf, remote-config, and storage packages.
|
@russellwheatley @mikehardy, here is the breakdown:
The catch is that these
This sounds more straightforward then writing an ObjC shim for each Firebase target. But I owe answers for #8933 (comment) and #8933 (comment) so I'm looking into those now as they seem related. |
…ailures
- Gate the test app's precompiled FirebaseFirestore CocoaPods pod behind
rnfirebase_spm_disabled? so it no longer collides with the SPM-resolved
copies of FirebaseCore/FirebaseCoreExtension/FirebaseCoreInternal/
FirebaseSharedSwift during Xcode Archive builds ("Multiple commands
produce ... FirebaseCore.framework").
- Link FirebaseCore directly onto the app's own target when SPM is active,
restoring the "free" linkage CocoaPods always gave apps whose native code
calls FIRApp/FIROptions APIs directly.
- Fix the SPM embed script to also search Xcode Archive's
UninstalledProducts folder, not just PackageFrameworks -- real
`xcodebuild archive` builds were silently embedding zero Firebase SPM
frameworks and would crash at launch.
…bled rnfirebase_add_spm_core_to_app_target writes its FirebaseCore package product dependency into the app's own Xcode project (testing.xcodeproj), a different project than the one React Native's own SPM integration manages and cleans up on every install (Pods.xcodeproj). Once that dependency had been committed into the app's .pbxproj from a prior SPM-mode `pod install`, switching to `$RNFirebaseDisableSPM = true` and reinstalling left the stale SPM wiring in place forever: the app target ended up linked against both Xcode's SPM-resolved firebase-ios-sdk package graph and the freshly CocoaPods-resolved Firebase/CoreOnly pod at the same time, and the two copies of Firebase's module graph collided -- "redefinition of module 'Firebase'" at compile time, and duplicate App-Intents-metadata build commands at Archive time. This broke the previously-green `iOS (debug, cocoapods)` and `iOS Release Archive (cocoapods)` CI jobs. Add rnfirebase_remove_spm_core_from_app_target as the counterpart to rnfirebase_add_spm_core_to_app_target: removes the stale product dependency (and, once nothing else references it, the package reference itself) whenever SPM is inactive. Also adds Minitest coverage for both functions -- rnfirebase_add_spm_core_to_app_target previously had none, which is how this regression went unnoticed.

Summary
firebase_dependency()helper (packages/app/firebase_spm.rb)spm_dependency). For older versions or when explicitly disabled ($RNFirebaseDisableSPM = true), CocoaPods is used as fallback.FirebaseCoreInternal,FirebaseSharedSwift) when using CocoaPodsChanges
packages/app/firebase_spm.rb— New helper withfirebase_dependency()function that auto-detects SPM supportpackages/app/package.json— AddedfirebaseSpmUrlfield as single source of truthfirebase_dependency()instead of directs.dependency#if __has_includeguards for dual SPM/CocoaPods importsdep-resolution: ['spm', 'cocoapods']dimension (4 job combinations)firebase_spm.rblogicdocs/ios-spm.mdwith architecture, integration guides, and troubleshootingHow it works
Decision logic:
spm_dependency()defined? (RN >= 0.75 injects it) → YES → use SPM$RNFirebaseDisableSPMset in Podfile? → YES → force CocoaPodsUser-facing configuration
SPM mode (default for RN >= 0.75):
CocoaPods mode (legacy/opt-out):
Xcode 26 workaround (both modes):
Test plan
packages/app/__tests__/firebase_spm_test.rb)$RNFirebaseDisableSPM = truecorrectly forces CocoaPodsrelated issue: #9010
Note
Medium Risk
Medium risk because it changes iOS dependency/linkage and native import behavior across many modules, which can break builds in certain Xcode/React Native configurations despite added CI coverage.
Overview
Introduces a centralized
firebase_dependency()helper (packages/app/firebase_spm.rb) that switches RNFirebase iOS Firebase dependencies between SPM (RN >= 0.75 by default) and CocoaPods (fallback or$RNFirebaseDisableSPM), with the SPM repo URL sourced frompackages/app/package.json.Updates all affected RNFirebase module podspecs to use the helper (including special-casing Analytics’ SPM vs CocoaPods behavior), adjusts numerous iOS sources to compile under both header layouts via
__has_include/module imports, and extends Crashlytics symbol upload scripting to find the SPM checkout.Expands iOS E2E CI to run a matrix over
spm/cocoapods×debug/release(including mode-specific Podfile edits and cache keys), adds Ruby unit tests for the helper and runs them in the Jest workflow, and adds new SPM documentation plus a local SPM verification screen.Reviewed by Cursor Bugbot for commit 496d97c. Bugbot is set up for automated code reviews on this repo. Configure here.