Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
3 changes: 3 additions & 0 deletions Firestore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ scripts/build.sh Firestore iOS spm

This is rarely necessary for primary development and is done automatically by CI.

For a detailed explanation of the Firestore target hierarchy in the
`Package.swift` manifest, see [FirestoreSPM.md](../docs/FirestoreSPM.md).

### Improving the debugger experience

You can install a set of type formatters to improve the presentation of
Expand Down
196 changes: 196 additions & 0 deletions docs/FirestoreSPM.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# Firestore Swift Package Manager Target Hierarchy

This document outlines the hierarchy of the Firestore-related targets in the
`Package.swift` manifest. The setup is designed to support three different
build options for Firestore: from a pre-compiled binary (the default), from
source (via the `FIREBASE_SOURCE_FIRESTORE` environment variable), or from a
local binary for CI purposes (via the `FIREBASECI_USE_LOCAL_FIRESTORE_ZIP`
environment variable).

---

## 1. Binary-based build (Default)

When the `FIREBASE_SOURCE_FIRESTORE` environment variable is **not** set, SPM
will use pre-compiled binaries for Firestore and its heavy dependencies. This
is the default and recommended approach for most users.

### Dependency hierarchy

The dependency tree for a binary-based build is as follows:

```
FirebaseFirestore (Library Product)
└── FirebaseFirestoreTarget (Wrapper Target)
└── FirebaseFirestore (Swift Target)
├── FirebaseAppCheckInterop
├── FirebaseCore
├── FirebaseCoreExtension
├── FirebaseSharedSwift
├── leveldb
├── nanopb
├── abseil (binary) (from https://github.com/google/abseil-cpp-binary.git)
├── gRPC-C++ (binary) (from https://github.com/google/grpc-binary.git, contains BoringSSL-GRPC target)
└── FirebaseFirestoreInternalWrapper (Wrapper Target)
└── FirebaseFirestoreInternal (Binary Target)
```

### Target breakdown

* **`FirebaseFirestore`**: The Swift target containing the public API. In this
configuration, it depends on the binary versions of abseil and gRPC, as
well as the `FirebaseFirestoreInternalWrapper`.
* **`FirebaseFirestoreInternalWrapper`**: A thin wrapper target that exists to
expose the headers from the underlying binary target.
* **`FirebaseFirestoreInternal`**: This is a `binaryTarget` that downloads and
links the pre-compiled `FirebaseFirestoreInternal.xcframework`. This
framework contains the compiled C++ core of Firestore.

---

## 2. Source-based build

When the `FIREBASE_SOURCE_FIRESTORE` environment variable is set, Firestore and
its dependencies (like abseil and gRPC) are compiled from source.

### How to build Firestore from source

To build Firestore from source, set the `FIREBASE_SOURCE_FIRESTORE` environment
variable before building the project.

#### Building with Xcode

A direct method for building within Xcode is to pass the environment variable
upon opening it from the command line. This approach scopes the variable to the
Xcode instance. To enable an env var within Xcode, first quit any running Xcode
instance, and then open the project from the command line:

```console
open --env FIREBASE_SOURCE_FIRESTORE Package.swift
```

To unset the env var, quit the running Xcode instance. If you need to pass
multiple variables, repeat the `--env` argument for each:
```console
open --env FIREBASECI_USE_LATEST_GOOGLEAPPMEASUREMENT \
--env FIREBASE_SOURCE_FIRESTORE Package.swift
```

#### Command-line builds

For command-line builds using `xcodebuild` or `swift build`, the recommended
approach is to prefix the build command with the environment variable. This sets
the variable only for that specific command, avoiding unintended side effects.

```bash
FIREBASE_SOURCE_FIRESTORE=1 xcodebuild -scheme FirebaseFirestore \
-destination 'generic/platform=iOS'
```

Alternatively, if you plan to run multiple commands that require the variable
to be set, you can `export` it. This will apply the variable to all subsequent
commands in that terminal session.

```bash
export FIREBASE_SOURCE_FIRESTORE=1
xcodebuild -scheme FirebaseFirestore -destination 'generic/platform=iOS'
# Any other commands here will also have the variable set
```

Once the project is built with the variable set, SPM will clone and build
Firestore and its C++ dependencies (like abseil and gRPC) from source.

### Dependency hierarchy

The dependency tree for a source-based build looks like this:

```
FirebaseFirestore (Library Product)
└── FirebaseFirestoreTarget (Wrapper Target)
└── FirebaseFirestore (Swift Target)
├── FirebaseCore
├── FirebaseCoreExtension
├── FirebaseSharedSwift
└── FirebaseFirestoreInternalWrapper (C++ Target)
├── FirebaseAppCheckInterop
├── FirebaseCore
├── leveldb
├── nanopb
├── abseil (source) (from https://github.com/firebase/abseil-cpp-SwiftPM.git)
└── gRPC-cpp (source) (from https://github.com/grpc/grpc-ios.git)
└── BoringSSL (source) (from https://github.com/firebase/boringSSL-SwiftPM.git)
```

### Target breakdown

* **`FirebaseFirestore`**: The main Swift target containing the public Swift
API for Firestore. It acts as a bridge to the underlying C++
implementation.
* **`FirebaseFirestoreInternalWrapper`**: This target compiles the core C++
source code of Firestore. It depends on other low-level libraries and C++
dependencies, which are also built from source.

---

## 3. Local binary build (CI only)

A third, less common build option is available for CI environments. When the
`FIREBASECI_USE_LOCAL_FIRESTORE_ZIP` environment variable is set, the build
system will use a local `FirebaseFirestoreInternal.xcframework` instead of
downloading the pre-compiled binary. This option assumes the xcframework is
located at the root of the repository.

This option is primarily used by internal scripts, such as
`scripts/check_firestore_symbols.sh`, to perform validation against a locally
built version of the Firestore binary. It is not intended for general consumer
use.

---

## Core target explanations

### `FirebaseFirestore` (Library product)

The main entry point for integrating Firestore via SPM is the
`FirebaseFirestore` library product.

```swift
.library(
name: "FirebaseFirestore",
targets: ["FirebaseFirestoreTarget"])
```

This product points to a wrapper target, `FirebaseFirestoreTarget`, which then
depends on the appropriate Firestore targets based on the chosen build option.

### `FirebaseFirestoreTarget` (Wrapper target)

The `FirebaseFirestoreTarget` is a thin wrapper that exists to work around a
limitation in SPM where a single target cannot conditionally depend on
different sets of targets (source vs. binary).

By having clients depend on the wrapper, the `Package.swift` can internally
manage the complexity of switching between source and binary builds. This
provides a stable entry point for all clients and avoids pushing conditional
logic into their own package manifests.

---

## Test targets

The testing infrastructure for Firestore in SPM is designed to be independent
of the build choice (source vs. binary).

* **`FirebaseFirestoreTestingSupport`**: This is a library target, not a test
target. It provides public testing utilities that consumers can use to
write unit tests for their Firestore-dependent code. It has a dependency on
`FirebaseFirestoreTarget`, which means it will link against whichever
version of Firestore (source or binary) is being used in the build.

* **`FirestoreTestingSupportTests`**: This is a test target that contains the
unit tests for the `FirebaseFirestoreTestingSupport` library itself. Its
purpose is to validate the testing utilities.

Because both of these targets depend on the `FirebaseFirestoreTarget` wrapper,
they seamlessly adapt to either the source-based or binary-based build path
without any conditional logic.
Loading