Skip to content

Commit a91593d

Browse files
russellwheatleymikehardy
authored andcommitted
docs(ios-spm): fix stale cross-refs, document Crashlytics/SPM and shape-check gaps
Point the "Release launch dyld failure" citations in verify-ios-release-archive.sh and tests_e2e_ios.yml at the actual "Runtime framework embedding" section (the heading was renamed without updating callers). Document firebase_spm_shape_test.rb in the app tests README, add the Crashlytics dSYM-under-SPM behavior and the missing archive-signature/FIRApp-link/stale-SPM-core troubleshooting entries to the consumer and OKF docs, and list configure-ios-dep-resolution.sh in the iOS CI helper scripts table.
1 parent 24c2724 commit a91593d

7 files changed

Lines changed: 59 additions & 2 deletions

File tree

.github/workflows/scripts/verify-ios-release-archive.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# https://github.com/invertase/react-native-firebase/pull/8933#issuecomment-4308578826
1515
# and the release+SPM dyld launch failure fixed by
1616
# packages/app/firebase_spm.rb's embed phase (see
17-
# okf-bundle/ios-spm-native-imports.md "Release launch dyld failure"), which
17+
# okf-bundle/ios-spm-native-imports.md "Runtime framework embedding"), which
1818
# was itself only ever validated against a Release-iphonesimulator build.
1919
#
2020
# This script does not require a signing identity or provisioning profile --

.github/workflows/tests_e2e_ios.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ jobs:
576576
# repo. This job produces a real, unsigned device archive (no signing
577577
# identity/provisioning profile needed) and verifies the resulting app
578578
# bundle is actually loadable. See .github/workflows/scripts/verify-ios-release-archive.sh
579-
# and okf-bundle/ios-spm-native-imports.md "Release launch dyld failure".
579+
# and okf-bundle/ios-spm-native-imports.md "Runtime framework embedding".
580580
ios-release-archive:
581581
name: iOS Release Archive (${{ matrix.dep-resolution }})
582582
runs-on: macos-26

.spellcheck.dict.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,6 @@ SPM
310310
SPM-only
311311
TestFlight
312312
tvOS
313+
xcframework
314+
xcframeworks
313315
YAML

docs/ios-spm.mdx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,29 @@ uses SPM, it does not fall back to CocoaPods.
183183
or continue with the automatic CocoaPods fallback.
184184
- **SPM and CocoaPods both produce the same Firebase framework:** Remove manually
185185
declared Firebase pods/packages so RNFB owns Firebase dependency resolution.
186+
- **Release archive fails with an `xcframework-ios.signature` "couldn't be
187+
copied ... because an item with the same name already exists" error:** a
188+
long-standing Xcode Archive bug with SPM binary xcframeworks, not an RNFB
189+
regression. RNFB works around it automatically; if the warning says it could
190+
not do so, add a Run Script build phase to your app target that removes the
191+
named artifact:
192+
193+
```bash
194+
rm -f "${CONFIGURATION_BUILD_DIR}"/<NameFromTheErrorMessage>.xcframework-ios.signature
195+
```
196+
197+
- **Archive fails with `Undefined symbols ... _OBJC_CLASS_$_FIRApp`:** RNFB
198+
links `FirebaseCore` into your app target automatically so the required
199+
`[FIRApp configure]`/`FirebaseApp.configure()` call links. This only
200+
surfaces if that automatic step could not run; call
201+
`rnfirebase_add_spm_core_to_app_target(installer)` in your `post_install`
202+
block as a fallback if your own native code also calls Firebase APIs
203+
directly.
204+
- **A stale "firebase-ios-sdk" Swift Package reference remains on the app
205+
target after switching from SPM to `$RNFirebaseDisableSPM = true`:** RNFB
206+
removes it automatically on the next `pod install`; if that could not run,
207+
remove the "firebase-ios-sdk" Swift Package dependency from the app target
208+
manually in Xcode.
186209

187210
After changing dependency resolution, verify both a Debug build and a Release
188211
archive. A simulator build alone does not exercise all framework-embedding and

okf-bundle/ci-workflows/ios.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ from this job is a known Xcode Archive bug, not a regression — see
525525
| `.github/workflows/scripts/wait-for-load-settle.sh` | `RNFB_LOAD_SETTLE_MAX_LOAD` (default **20**), `RNFB_LOAD_SETTLE_MAX_WAIT_SEC`, `RNFB_LOAD_SETTLE_POLL_SEC` | Poll host load immediately before Detox |
526526
| `.github/workflows/scripts/resource-monitor.sh` | `RNFB_RESOURCE_MONITOR_INTERVAL_SEC` (default 10), `RNFB_RESOURCE_MONITOR_LOG` | Background `uptime` + `ps` snapshots during Detox |
527527
| `.github/workflows/scripts/flake-summary.sh` | `RNFB_DETOX_LOG`, `RNFB_FLAKE_SUMMARY_OUT` | Post-run `rg` digest → `flake-summary.txt` |
528+
| `.github/workflows/scripts/configure-ios-dep-resolution.sh <spm\|cocoapods> [podfile-dir]` || Grep-verified `tests/ios/Podfile` patch for the `dep-resolution` matrix leg; shared by the `ios` job and `ios-release-archive` job so they can't drift apart |
528529

529530
Detox steps use `tee detox-step.log` and `exit ${PIPESTATUS[0]}` so the artifact preserves full output while the step still fails correctly.
530531

okf-bundle/ios-spm-native-imports.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,22 @@ Firebase's component runtime, so they were moved to a private class extension in
135135
`RNFBCrashlyticsInitProvider.m`. This keeps the header imported by `.mm` code
136136
Firebase-free without inventing a dependency on a private/transitive target.
137137

138+
## Crashlytics dSYM upload under SPM
139+
140+
`packages/crashlytics/ios_config.sh` (the dSYM/symbol-upload script consumers
141+
wire into an Xcode "Run Script" build phase) only knew two locations for
142+
Crashlytics' `upload-symbols`/`run` tool: `$PODS_ROOT/FirebaseCrashlytics/run`
143+
and a manually-vendored `FirebaseCrashlytics.framework/run`. Neither path
144+
exists under SPM, so symbol upload silently no-opped for SPM users.
145+
146+
SPM checks out `upload-symbols` under
147+
`DerivedData/<Project>/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/upload-symbols`.
148+
`BUILD_DIR` is typically `.../DerivedData/<Project>/Build/Products`, so the
149+
script strips from `/Build` onward to reach the `SourcePackages` checkout
150+
root and calls that binary directly (`chmod +x` first if the checkout didn't
151+
preserve the executable bit). If none of the three paths resolve, the script
152+
warns and lists every path it checked rather than failing silently.
153+
138154
## Runtime framework embedding
139155

140156
React Native's `spm_dependency` integration attaches SPM products to pod

packages/app/__tests__/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@ Unit tests for `firebase_spm.rb` — the shared helper that declares Firebase de
88
ruby __tests__/firebase_spm_test.rb
99
```
1010

11+
### Shape-check suite (`firebase_spm_shape_test.rb`)
12+
13+
A companion, opt-in Minitest suite — not a replacement for `firebase_spm_test.rb`. `firebase_spm_test.rb` mocks the `Xcodeproj`/`CocoaPods` classes `firebase_spm.rb` depends on (`MockAggregateTarget`, `MockInstaller`, `MockRootObject`, `MockBuildConfig`, `MockTarget`, `MockUserProject`, `MockBuildType`, etc.) so the helper's post-install logic can be unit-tested without those gems installed. A mock is only ever as accurate as the person who wrote it, and this exact PR shipped a bug of that class (`MockAggregateTarget#build_as_static?` modeled as a directly-settable flag, when the real `Pod::Target#build_as_static?` is unconditionally `true` — the actual per-install signal is `target_definition.build_type.static?`), which only surfaced via a real `pod install`.
14+
15+
`firebase_spm_shape_test.rb` asserts, against the **real** `xcodeproj`/`cocoapods` gems, that every mocked class/method in `firebase_spm_test.rb` still has the shape those mocks assume — so a future CocoaPods/Xcodeproj release that changes that shape fails in seconds instead of ~20 minutes into a real `pod install` in the E2E jobs.
16+
17+
It skips cleanly (no failure, no tests defined) when `xcodeproj`/`cocoapods` aren't installed, so it is always safe to run unconditionally:
18+
19+
```bash
20+
ruby __tests__/firebase_spm_shape_test.rb
21+
```
22+
23+
- **"Test Firebase SPM Helper" step** (`tests_jest.yml`) never installs these gems — the suite is expected to skip there.
24+
- **"Verify Firebase SPM Xcodeproj/CocoaPods API shape" step** (`tests_e2e_other.yml`, `other` job) does have them and runs the suite for real, right after that job's existing `gem update cocoapods xcodeproj` step — no new CI job, no new gem installs.
25+
1126
### What is `Pod::Specification` and why is it mocked?
1227

1328
`Pod::Specification` is the core CocoaPods class — it's the `s` object used inside every `.podspec` file to declare things like `s.dependency`, `s.name`, `s.version`, etc. We mock it with a simple class that records `dependency` calls, so we can run the tests without installing CocoaPods.

0 commit comments

Comments
 (0)