Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/linting.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ jobs:
command: yarn
- name: Lint
run: yarn tsc:compile
- name: Test-expo TypeScript
run: yarn tsc:compile:test-expo
# https://github.com/actions/cache/releases
- uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
name: Yarn Cache Save
Expand Down
52 changes: 48 additions & 4 deletions .github/workflows/scripts/test-expo-ios-link.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
# Regression #9202 signature: duplicate `_FIRFirebaseVersion` from both
# libRNFBApp.a(FirebaseCore.o) and libRNFBMessaging.a(FirebaseCore.o). The
# green graph must produce RNFB frameworks and link them with `-framework`.
# The dynamic-framework / static-archive check below discovers and covers
# every generated `RNFB*` CocoaPods target, not just App and Messaging.
set -euo pipefail

cd "$(dirname "$0")/../../.."
Expand Down Expand Up @@ -173,7 +175,50 @@ pod_framework_file_ref_ok() {
' "$PODS_PBXPROJ"
}

for rnfb_target in RNFBApp RNFBMessaging; do
# Discover every generated `RNFB<Name>` PBXNativeTarget from the Pods
# project, scoped to the PBXNativeTarget section only (other sections, e.g.
# PBXBuildFile / PBXFrameworksBuildPhase, reference names like
# "RNFBApp.framework in Frameworks" that would false-match if unscoped).
# POSIX/BSD-awk-safe: no gawk-only match()-with-capture-array, just
# sub()/index() the way pod_target_product_type / pod_framework_file_ref_ok
# already do above.
discover_rnfb_targets() {
awk '
/\/\* Begin PBXNativeTarget section \*\// { in_section = 1; next }
/\/\* End PBXNativeTarget section \*\// { in_section = 0 }
in_section && /\/\* RNFB[A-Za-z0-9]+ \*\/ = \{/ {
line = $0
sub(/^.*\/\* /, "", line)
sub(/ \*\/ = \{.*$/, "", line)
print line
}
' "$PODS_PBXPROJ" | sort -u
}

rnfb_targets=()
while IFS= read -r rnfb_target_name; do
[[ -n "$rnfb_target_name" ]] && rnfb_targets+=("$rnfb_target_name")
done < <(discover_rnfb_targets)

if [[ "${#rnfb_targets[@]}" -eq 0 ]]; then
log "ERROR: discovered zero RNFB* targets in ${PODS_PBXPROJ} -- this almost certainly means the discovery pattern broke, not that there are no RNFB pods"
exit 1
fi

log "discovered RNFB targets: ${rnfb_targets[*]}"

has_rnfb_app=0
has_rnfb_messaging=0
for rnfb_target_name in "${rnfb_targets[@]}"; do
[[ "$rnfb_target_name" == "RNFBApp" ]] && has_rnfb_app=1
[[ "$rnfb_target_name" == "RNFBMessaging" ]] && has_rnfb_messaging=1
done
if [[ "$has_rnfb_app" -ne 1 || "$has_rnfb_messaging" -ne 1 ]]; then
log "ERROR: discovery did not find both RNFBApp and RNFBMessaging (found: ${rnfb_targets[*]}) -- these two ship in every graph, so this is a discovery bug, not an empty RNFB graph"
exit 1
fi

for rnfb_target in "${rnfb_targets[@]}"; do
product_type="$(pod_target_product_type "$rnfb_target")"
if [[ "$product_type" != "com.apple.product-type.framework" ]]; then
log "ERROR: ${rnfb_target} generated with wrong product type '${product_type:-missing}' (expected dynamic framework)"
Expand All @@ -186,7 +231,7 @@ for rnfb_target in RNFBApp RNFBMessaging; do
done

app_ldflags="$(grep '^OTHER_LDFLAGS = ' "$PODS_XCCONFIG" || true)"
for rnfb_target in RNFBApp RNFBMessaging; do
for rnfb_target in "${rnfb_targets[@]}"; do
if grep -Fq -- "-l\"${rnfb_target}\"" <<<"$app_ldflags"; then
log "ERROR: app link inputs still use static library -l\"${rnfb_target}\""
exit 1
Expand Down Expand Up @@ -252,8 +297,7 @@ if [[ "$xcodebuild_status" -ne 0 ]]; then
fi

if grep -q "duplicate symbol '_FIRFirebaseVersion'" "$XCODEBUILD_LOG" ||
grep -q 'libRNFBApp\.a.*FirebaseCore\.o' "$XCODEBUILD_LOG" ||
grep -q 'libRNFBMessaging\.a.*FirebaseCore\.o' "$XCODEBUILD_LOG"; then
grep -E -q 'libRNFB[A-Za-z0-9]+\.a.*FirebaseCore\.o' "$XCODEBUILD_LOG"; then
log "ERROR: xcodebuild passed but the #9202 static-archive signature remains in its log"
exit 1
fi
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -625,10 +625,13 @@ Package.Resolved

# Generated Expo prebuild / pods / local modules (test-expo fixture sources stay tracked)
test-expo/ios/
test-expo/android/
test-expo/node_modules/
test-expo/.expo/
# Fake Expo fixture plist (global GoogleService-Info.plist rule would drop it)
# Fake Expo fixture plist / json (global GoogleService-Info.plist and
# google-services.json rules would drop them)
!test-expo/GoogleService-Info.plist
!test-expo/google-services.json

# RN CLI prebuilt RNCore compile fixture: own ios/ sources, never android/ or Pods
test-rn-bare/android/
Expand Down
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ tests/node_modules/**
tests/app.playground.js
tests/app.smartreply.js
tests/app.admob.js
test-expo/ios/**
test-expo/android/**
test-expo/.expo/**

/.nx/workspace-data
2 changes: 2 additions & 0 deletions docs/ai/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ If you need to call the Gemini API directly from your mobile or web app — rath

# Usage

See the Expo example at [`test-expo/app/ai.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/ai.tsx).

## Generate text from text-only input

You can call the Gemini API with input that includes only text. For these calls, you need to use a model that supports text-only prompts (like Gemini 3.1 Flash-Lite).
Expand Down
2 changes: 2 additions & 0 deletions docs/analytics/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ Analytics automatically logs some [events](https://support.google.com/analytics/

# Usage

See the Expo example at [`test-expo/app/analytics.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/analytics.tsx).

Analytics offers a wealth of [Predefined Events](/analytics/usage#predefined-events) to track user behavior. Analytics also offers folks the ability to log [Custom Events](/analytics/usage#custom-events) . If you're already familiar with Google Analytics, this method is equivalent to using the event command in [gtag.js](https://developers.google.com/gtagjs/).

## Event Parameters
Expand Down
2 changes: 2 additions & 0 deletions docs/app-check/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ The [official Firebase App Check documentation](https://firebase.google.com/docs

# Usage

See the Expo example at [`test-expo/app/app-check.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/app-check.tsx).

## Register Firebase Apps

Before the App Check package can be used on iOS or Android, the corresponding App must be registered in the firebase console.
Expand Down
2 changes: 2 additions & 0 deletions docs/app/usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ The App module is available by default once you have installed the React Native
- Creating [Secondary Firebase App Instances](/app/usage#secondary-apps).
- Exposing [Utilities](/app/utils) to aid development.

See the Expo example at [`test-expo/app/app.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/app.tsx).

# Platform support and New Architecture

| | |
Expand Down
2 changes: 2 additions & 0 deletions docs/auth/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Firebase Authentication integrates tightly with other Firebase services, and it

# Usage

See the Expo example at [`test-expo/app/auth/index.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/auth/index.tsx).

## Listening to authentication state

In most scenarios using Authentication, you will want to know whether your users are currently signed-in or signed-out
Expand Down
2 changes: 2 additions & 0 deletions docs/crashlytics/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ To learn more, view the [Firebase Crashlytics documentation](https://firebase.go

# Usage

See the Expo example at [`test-expo/app/crashlytics.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/crashlytics.tsx).

Use the `log` method throughout your app to accumulate extra context for possible crashes that can happen. For additional context, Crashlytics also offers [various methods](/crashlytics/usage#crash-attributes) to set attributes for the crash report. You can also test Crashlytics by forcing a crash through the `crash` method.

Crashlytics also supports sending JavaScript stack traces to the Firebase console. This can be used in any situation where an error occurs but is caught by your own code to recover gracefully. To send a stack trace, pass a JavaScript Error to the `recordError` method.
Expand Down
2 changes: 2 additions & 0 deletions docs/database/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ To learn more, view the [Firebase Realtime Database documentation](https://fireb

# Usage

See the Expo example at [`test-expo/app/database.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/database.tsx).

## References

A core concept to understanding Realtime Database are references - a reference to a specific node within your database. A node
Expand Down
2 changes: 2 additions & 0 deletions docs/firestore/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ latency or Internet connectivity.

# Usage

See the Expo example at [`test-expo/app/firestore.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/firestore.tsx).

## Collections & Documents

Cloud Firestore stores data within "documents", which are contained within "collections", and documents can also contain
Expand Down
2 changes: 2 additions & 0 deletions docs/functions/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ For more information on use cases, view the [Firebase Cloud Functions](https://f

# Usage

See the Expo example at [`test-expo/app/functions.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/functions.tsx).

The Cloud Functions module provides the functionality to directly trigger deployed HTTPS callable functions, without worrying
about security or implementing a HTTP request library.

Expand Down
2 changes: 2 additions & 0 deletions docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Integration with Expo is possible when using a [development build](https://docs.

_NOTE:_ React Native Firebase cannot be used in the pre-compiled [Expo Go app](https://docs.expo.dev/workflow/overview/#expo-go-an-optional-tool-for-learning) because React Native Firebase uses native code that is not compiled into Expo Go.

See the Expo example at [`test-expo/`](https://github.com/invertase/react-native-firebase/tree/main/test-expo).

> **Warning:** If you are using `expo-dev-client`, native crashes (such as those triggered by `crash(getCrashlytics())`) will **not** be reported to Firebase Crashlytics during development. This is because `expo-dev-client` provides a custom error overlay that catches and displays errors before they are sent to Firebase. To test native crash reporting, you must remove `expo-dev-client` and run your app in a standard release or debug build without the custom error overlay.

To create a new Expo project, see the [Get started](https://docs.expo.dev/get-started/create-a-project/) guide in Expo documentation.
Expand Down
2 changes: 2 additions & 0 deletions docs/installations/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ Each configured `FirebaseApp` has a corresponding single instance of Installatio

# Usage

See the Expo example at [`test-expo/app/installations.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/installations.tsx).

Please see the API Reference for detailed usage information on the available APIs
2 changes: 2 additions & 0 deletions docs/messaging/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ The module also provides basic support for displaying local notifications, to le

# Usage

See the Expo example at [`test-expo/app/messaging.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/messaging.tsx).

## iOS - Requesting permissions

> **Deprecated:** `requestPermission`, `hasPermission`, and `AuthorizationStatus` are deprecated. Use [react-native-permissions](https://github.com/zoontek/react-native-permissions) or [expo-notifications](https://docs.expo.dev/versions/latest/sdk/notifications/) instead. See [issue #6283](https://github.com/invertase/react-native-firebase/issues/6283).
Expand Down
2 changes: 2 additions & 0 deletions docs/perf/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ app code. All performance metrics are available on your Firebase [console](https

# Usage

See the Expo example at [`test-expo/app/perf.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/perf.tsx).

## Custom tracing

Below is how you would measure the amount of time it would take to complete a specific task in your app code.
Expand Down
2 changes: 2 additions & 0 deletions docs/phone-number-verification/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Common reasons verification may be unsupported:

# Usage

See the Expo example at [`test-expo/app/phone-number-verification.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/phone-number-verification.tsx).

## Check verification support

Before attempting verification, check if the device's SIM card(s) support phone number verification. This call does not require user consent and can be called freely:
Expand Down
2 changes: 2 additions & 0 deletions docs/remote-config/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ over when and how these Remote Config values are applied and affect your applica

# Usage

See the Expo example at [`test-expo/app/remote-config.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/remote-config.tsx).

To get started, you need to define some parameters over on the [Firebase Console](https://console.firebase.google.com/project/_/config).

![Firebase Console - Remote Config](https://images.prismic.io/invertase/87dc40bd-0da7-4d83-a87c-b12698b9818f_remote-config-console.png?auto=compress,format)
Expand Down
2 changes: 2 additions & 0 deletions docs/storage/usage/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ global redundancy. Storage lets you securely upload these files directly from mo

# Usage

See the Expo example at [`test-expo/app/storage.tsx`](https://github.com/invertase/react-native-firebase/blob/main/test-expo/app/storage.tsx).

Your files are stored in a Google Cloud Storage bucket. The files in this bucket are presented in a hierarchical structure,
just like a file system. By creating a reference to a file, your app gains access to it. These references can then be
used to upload or download data, get or update metadata or delete the file. A reference can either point to a specific
Expand Down
5 changes: 4 additions & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ export default defineConfig([
'packages/**/dist/',
'packages/**/android/build/',
'**/type-test.ts',
'packages/ai/__tests__/test-utils'
'packages/ai/__tests__/test-utils',
'test-expo/ios/',
'test-expo/android/',
'test-expo/.expo/',
]),

{
Expand Down
4 changes: 2 additions & 2 deletions okf-bundle/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ okf_version: '0.1'

# Testing

- [Agent command policy](/testing/agent-command-policy.md) — allowlisted shell commands for agents (install, prepare, validation, e2e, Expo documented-path iOS **link**, RN CLI prebuilt RNCore iOS **build**; not Detox)
- [Agent command policy](/testing/agent-command-policy.md) — allowlisted shell commands for agents (install, prepare, validation, e2e, Expo example typecheck + documented-path iOS **link**, RN CLI prebuilt RNCore iOS **build**; not Detox)
- [Change authoring workflow](/testing/change-authoring-workflow.md) — verified product change loop (unit-focused → `documentation?` → area-focused `independent-review` → commit); [§ validation evidence (blocking)](testing/change-authoring-workflow.md#validation-evidence-blocking); [coverage evidence package](testing/coverage-design.md#coverage-evidence-package)
- [Iteration vocabulary](/testing/iteration-vocabulary.md) — work type, tier, and queue field identifiers
- [Running e2e tests](/testing/running-e2e.md) — canonical e2e commands, narrowing, environment, diagnosis; [§ test-app native modules](testing/running-e2e.md#test-app-native-modules); [§ slot lifecycle](testing/running-e2e.md#slot-lifecycle)
Expand All @@ -36,7 +36,7 @@ okf_version: '0.1'
# Packages

- [AI](/packages/ai/index.md) — Agent Platform / Vertex backend naming, compare-types, generative models
- [App](/packages/app/index.md) — core app / Expo plugin / iOS SPM helpers; Expo documented-path iOS link (`test-expo/`); RN CLI prebuilt RNCore iOS compile (`test-rn-bare/`)
- [App](/packages/app/index.md) — core app / Expo plugin / iOS SPM helpers; Expo example + documented-path iOS link (`test-expo/`); RN CLI prebuilt RNCore iOS compile (`test-rn-bare/`)
- [App Check](/packages/app-check/index.md) — iOS provider-factory init (pending + fail-closed), ADRs + work queue for #9116
- [Auth](/packages/auth/index.md) — modular API type parity, platform matrix, `compare:types`
- [Firestore](/packages/firestore/index.md) — Pipelines architecture, parity, e2e coverage
Expand Down
27 changes: 18 additions & 9 deletions okf-bundle/ios-spm-native-imports.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ type: Reference
title: iOS SPM native integration decisions
description: Why RNFB uses dual imports, Objective-C helpers for Swift Firebase products, and an app framework-embedding phase.
tags: [ios, spm, cocoapods, imports, firebase, cxx-modules]
timestamp: 2026-08-06T16:00:00Z
timestamp: 2026-09-10T00:00:00Z
---

# iOS SPM native integration decisions
Expand Down Expand Up @@ -242,8 +242,9 @@ GitHub [#9158](https://github.com/invertase/react-native-firebase/issues/9158)

Maintainer check of the **Expo documented path** (SPM + dynamic frameworks +
prebuild-generated AppDelegate `FIRApp` call) is **`yarn test-expo:ios:link`**
only — [agent command policy](testing/agent-command-policy.md). That is a
workspace **link** fixture (`test-expo/`), not Detox e2e (`yarn tests:ios:*`).
only — [agent command policy](testing/agent-command-policy.md). `test-expo/` is
also the user-facing Expo example; the closer is still a workspace **link**
gate (build-only, does not launch), not Detox e2e (`yarn tests:ios:*`).
Do not restate `expo prebuild` / `xcodebuild` here. Package index:
[App package](packages/app/index.md).

Expand Down Expand Up @@ -466,10 +467,18 @@ Expo paths do not warn.

The documented Podfile configuration does not change: SPM on,
`use_frameworks! :linkage => :dynamic`, prebuilt RNCore on. The canonical regression
fixture is **`yarn test-expo:ios:link`** ([agent command policy](testing/agent-command-policy.md)).
Link success confirms both RNFB framework products are in dynamic form and
duplicate Firebase symbols are absent, while still validating the app target's
own FirebaseCore dependency (the original purpose of that fixture). See
closer is **`yarn test-expo:ios:link`** ([agent command policy](testing/agent-command-policy.md)).
That command is **build-only** (`xcodebuild build`; it does not launch the app).
Link success confirms every discovered `RNFB*` CocoaPods product is a framework
and duplicate Firebase symbols are absent, while still validating the app target's
own FirebaseCore dependency. Discovery is dynamic against the generated Pods
project. Packages with no iOS native target (pure-JS `packages/ai`, Android-only
`packages/phone-number-verification`) do not produce an `RNFB*` product and are
not in that graph. Named App+Messaging GitHub
[#9158](https://github.com/invertase/react-native-firebase/issues/9158) /
[#9202](https://github.com/invertase/react-native-firebase/issues/9202)
signatures remain nested inside that generic gate. Do not hard-code an inventory
name list here. See
[Maintainer check of the Expo documented path](#app-target-firebasecore-link-package-dependency-alone-is-not-enough).
The [#9202](https://github.com/invertase/react-native-firebase/issues/9202)
regression signature is duplicate `_FIRFirebaseVersion` symbols from
Expand Down Expand Up @@ -517,8 +526,8 @@ invariants:
job verifies that every `@rpath` framework dependency is embedded;
- when Expo precompiled modules are active, `rnfirebase_restore_dynamic_linkage_after_expo_prebuilt!`
still restores RNFB targets from static back to dynamic if Expo's pre-install
hook downgraded them, and the `test-expo:ios:link` fixture still passes with no
duplicate Firebase symbols.
hook downgraded them, and `yarn test-expo:ios:link` still passes with every
discovered `RNFB*` product a framework and no duplicate Firebase symbols.

The bullets above are the SPM-specific review checklist. General build, lint,
and evidence requirements are owned by the
Expand Down
Loading
Loading