Skip to content

feat(test-expo): add Expo Router example with a route per package - #9313

Open
russellwheatley wants to merge 20 commits into
mainfrom
feature/cprn-317-expo-example-app-with-a-route-per-package
Open

russellwheatley wants to merge 20 commits into
mainfrom
feature/cprn-317-expo-example-app-with-a-route-per-package

Conversation

@russellwheatley

@russellwheatley russellwheatley commented Sep 10, 2026

Copy link
Copy Markdown
Member

Turns workspace test-expo/ into a user-facing Expo Router example (one route per included package, Auth as the only full flow) while keeping yarn test-expo:ios:link as the documented-path iOS closer on the widened native graph.

  • Expo Router TypeScript shell, remaining inventory deps and plugins
  • Generic RNFB* framework closer, with the named App+Messaging signatures nested
  • Email/anonymous Auth plus thin modular screens for the rest of the inventory, pointed at the real react-native-firebase-testing project on both platforms (matching tests/'s registered apps) instead of fake credentials
  • App Check screen initializes once instead of on every button press
  • Crashlytics dSYM run/upload warns instead of failing the build on rejection, consistently across CocoaPods, framework, and SPM, with test coverage added for all three
  • OKF records test-expo/ as the full-package example; usage-page permalinks pulled back for now, pending a more comprehensive follow-up PR

Maintainer note: Fixes internal CPRN-317

CI note: Create patch-package Patches is flaky, unrelated to this PR. The nested yarn add calls inside its Create Patches step aren't wrapped in the retry action the root Yarn Install step uses, so a registry hiccup (seen: registry.yarnpkg.com 502) or a runner comms timeout fails the whole job. Same commit passed on the parallel push-triggered run of the same workflow.

@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 69.81%. Comparing base (fa3f29d) to head (36322bf).

Additional details and impacted files
@@            Coverage Diff            @@
##               main    #9313   +/-   ##
=========================================
  Coverage     69.81%   69.81%           
  Complexity     2129     2129           
=========================================
  Files           439      439           
  Lines         25518    25518           
  Branches       4257     4257           
=========================================
  Hits          17814    17814           
  Misses         6360     6360           
  Partials       1344     1344           
Flag Coverage Δ
android-native 65.91% <ø> (ø)
e2e-ts-android 54.42% <ø> (ø)
e2e-ts-ios 53.89% <ø> (ø)
e2e-ts-macos 49.95% <ø> (ø)
ios-ruby 100.00% <ø> (ø)
jest 48.83% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@russellwheatley
russellwheatley force-pushed the feature/cprn-317-expo-example-app-with-a-route-per-package branch from 6b5f218 to fa866f6 Compare September 21, 2026 10:22
…t project

- add `test-expo:ios` / `test-expo:android` root scripts, plus matching `ios`/`android` scripts in `test-expo/package.json`
- point `GoogleService-Info.plist` at the real `react-native-firebase-testing` project instead of the placeholder fixture
- drop the generated `expo-env.d.ts` from `tsconfig.json`'s include list
- add shared `theme`, `AppButton`/`LinkButton`, and `TextField` primitives
- rework `ScreenChrome` with safe area, scroll, a real run button, and result/error banners
- turn the home route list into a tappable card list
- swap raw `Button`/`TextInput`/`Link` for the new primitives in the auth screens
- move row layout into an explicit inner `View` so label/chevron sit on one line, vertically centered, instead of stacking (the `Pressable` function-style prop wasn't applying flex row layout through `Link asChild`)
- bump subtitle, row label, and chevron font sizes
- center the list card with a max width and add a subtle shadow
Both platforms' Firebase config now point at real, already-registered
apps in the react-native-firebase-testing project instead of a mix of
real (iOS) and fake (Android) credentials:

- ios.bundleIdentifier -> io.invertase.testing (matches the committed
  GoogleService-Info.plist BUNDLE_ID, reusing tests/ios's registration)
- android.package -> com.invertase.testing, with google-services.json
  copied from tests/android/app (matches its registration)
Every screen carried a 'PROJECT_ID=test-expo-fixture-fake is a
placeholder' note that's no longer true after pointing the app at the
real react-native-firebase-testing project. Drops it from all 14
screens (keeping the unrelated expo-dev-client and Android-only notes
on crashlytics/phone-number-verification), and simplifies
authErrorMessage.ts to a plain error.message extractor since the
placeholder-config error mapping it did is no longer applicable.
initializeAppCheck was called inside handleRun, so every press after
the first re-initialized App Check for the same app. Native
installAppCheckProviderFactory can't be installed twice, so the
second press surfaced an internal-error instead of a token. Memoize
the instance at module scope and initialize lazily on first press.
russellwheatley added a commit that referenced this pull request Sep 22, 2026
…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 undefined/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).

Widened test-expo to the full first-party package list from #9313 (ai,
analytics, app-check, auth, crashlytics, database, firestore, functions,
installations, messaging, perf, phone-number-verification, remote-config,
storage) and generalized the closer to discover every RNFB* target
dynamically instead of hardcoding Analytics/Messaging, so the #9322 check
covers the real graph new packages land in, not just the two pods that
happened to need an explicit spm_dependency here. Confirmed clean across the
full 13-target graph: no new spm_dependency needed anywhere else, every
watched GUL class (including the two SWIFTPM_MODULE_BUNDLER_FINDER finder
symbols) has exactly one owner, never an RNFB* framework.

Adding crashlytics to test-expo surfaced two unrelated, real bugs in
packages/crashlytics/ios_config.sh that had to be fixed for the closer to
reach its own #9322 diagnosis at all: the dSYM plist lookup for Expo's
target-folder layout (already fixed upstream on main by #9304, just needed
a rebase here) and upload-symbols having no non-fatal handling under set -e,
so any upload failure (no network, placeholder credentials, project
mismatch) hard-failed the whole xcodebuild instead of just warning. The
non-fatal-upload fix is also tracked and PR'd standalone as #9333; carrying
it here too since this closer needs it in the meantime, and it'll collapse
into a no-op once #9333 merges and this branch rebases.

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.
@russellwheatley
russellwheatley marked this pull request as ready for review September 22, 2026 14:41
Removes the 'See the Expo example at ...' links added to docs/index.mdx
and the 16 package docs/*/usage pages. A follow-up PR will cover this
more comprehensively.
This PR already softened the SPM upload-symbols path so a rejected
upload warns instead of hard-failing the build via set -e. Apply the
same policy to the CocoaPods and vendored-framework run paths so all
three toolchains behave consistently, and add tests for the new
warn-on-failure branches (previously only the plist-lookup behavior
had coverage; the CocoaPods/framework branches had none).

@mikehardy mikehardy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ran the new test-expo app on iOS (slot-2). It launches cleanly, the Invertase dark/honey styling reads well, and the official logomark as the app icon makes the example feel like a real starting point. This is going to be a big developer help for people wiring RNFB into Expo.

This branch has not been deployed

No deployments
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.

🔥 [iOS][SPM][Crashlytics] dSYM upload-symbols script hardcodes ${PROJECT_DIR}/GoogleService-Info.plist, ignoring Expo/target-folder location

2 participants