Skip to content

feat(spm): add trait to opt out of Firestore - #16684

Merged
andrewheard merged 11 commits into
mainfrom
ah/swiftpm-traits
Sep 24, 2026
Merged

andrewheard merged 11 commits into
mainfrom
ah/swiftpm-traits

Conversation

@andrewheard

@andrewheard andrewheard commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Added support for configuring the Firebase Swift Package using Traits (SE-0450) and introduced a default-enabled Firestore trait.

Previously, adding the firebase-ios-sdk Swift package would always download and resolve Firestore's underlying dependencies (gRPC, abseil), even if the app didn't include Firestore. With package traits, you can now opt out of Firestore to reduce package resolution time, checkout size, and project overhead. The trait is enabled by default to preserve backwards compatibility.

Developer Instructions

Configuring Traits in Xcode (Xcode 26.4+)
  1. In Xcode, select your project in the Project Navigator.

  2. Select your project in the project editor and open the Package Dependencies tab.

  3. In the package list, locate Firebase and find the Traits column.

  4. Click the Traits dropdown (which shows default (Firestore) by default).

  5. Uncheck default (or uncheck Firestore). The dropdown will display None when all traits are disabled.

Configuring Traits in Package.swift

In your Package.swift, pass an empty traits array (or list only the traits you wish to enable) in your .package dependency declaration:

// swift-tools-version: 6.1
import PackageDescription

let package = Package(
  name: "MyPackage",
  dependencies: [
    .package(
      url: "https://github.com/firebase/firebase-ios-sdk.git",
      from: "13.0.0",
      traits: [
        // Omit "Firestore" (or leave empty) to prune Firestore, gRPC, and Abseil dependencies.
      ]
    ),
  ],
  targets: [
    .target(
      name: "MyTarget",
      dependencies: [
        .product(name: "FirebaseAuth", package: "firebase-ios-sdk"),
      ]
    ),
  ]
)

Technical Details

Implementation

1. Package Trait Definition & Target Gating

  • Trait declaration: Declared let firestoreTrait = Trait(name: "Firestore", ...) and enabled it by default in packageTraits via .default(enabledTraits: [firestoreTrait.name]).
  • Target dependency conditions: Conditioned source and binary Firestore targets and their heavy third-party dependencies (abseil-cpp-binary, grpc-binary, FirebaseFirestoreInternalWrapper, abseil-cpp-SwiftPM, grpc-ios, and FirebaseFirestoreCombineSwift) on .when(traits: ["Firestore"]).

2. C Preprocessor Macro (FIRESTORE_TRAIT_ENABLED)

  • SwiftPM traits pass -D <Trait> flags to swiftc for Swift conditional compilation, but do not automatically pass preprocessor defines to Clang for C/Objective-C compilation.
  • Added cSettings: [.define("FIRESTORE_TRAIT_ENABLED", .when(traits: ["Firestore"]))] on FirebaseFirestoreTarget and objc-import-test so Clang can conditionally compile Firestore-related code based on the trait.

3. Compile-Time Diagnostic Guard

  • In SwiftPM-PlatformExclude/FirebaseFirestoreWrap/dummy.m, added a #if !defined(FIRESTORE_TRAIT_ENABLED) #error check to provide an immediate, actionable diagnostic if an app target attempts to link FirebaseFirestoreTarget without enabling the Firestore trait.

4. Integration Tests & Conditional Imports

  • In SwiftPMTests/swift-test/all-imports.swift, guarded import FirebaseFirestore behind #if Firestore // Gated by the "Firestore" SwiftPM package trait..
  • In SwiftPMTests/objc-import-test/objc-header.m and objc-module.m, guarded imports behind #if defined(FIRESTORE_TRAIT_ENABLED).
  • Verified that test targets build and pass both with default traits enabled and with --disable-default-traits.

5. CocoaPods & Combine Compatibility

  • Preserved #if canImport(Combine) && swift(>=5.0) guards in FirebaseCombineSwift/Sources/Firestore source files to avoid breaking CocoaPods CI workflows (sdk.combine.yml), keeping trait gating at the target dependency level in Package.swift.

6. Integration Test App Opt-Out

  • Configured FirebaseAI/Tests/TestApp/FirebaseAITestApp.xcodeproj/project.pbxproj to opt out of default package traits (traits = ()), validating trait opt-out in Xcode while avoiding unnecessary Firestore dependencies for AI Logic integration tests.
Testing
  • Verified that swift test --test-product swift-test passed with default traits enabled.
  • Verified that swift test --disable-default-traits --test-product swift-test passed with traits disabled.
  • Verified that swift test --test-product objc-import-test passed with default traits enabled.
  • Verified that swift test --disable-default-traits --test-product objc-import-test passed with traits disabled.
  • Verified that compiling FirebaseFirestoreTarget with --disable-default-traits triggered the expected diagnostic in dummy.m.
  • Verified in a standalone consumer app depending on FirebaseAuth with traits: [] that abseil-cpp-binary and grpc-binary were completely pruned and never downloaded during resolution.

Add support for Swift Package Traits (SE-0450) by defining a default-
enabled "FirebaseFirestore" trait.

Previously, depending on the Firebase package pulled in Cloud Firestore
and its heavy dependencies (gRPC, Abseil) regardless of whether an app
used Firestore.

With this trait, developers can opt out in their Package.swift or via
Xcode's Package Dependencies UI to avoid fetching and downloading
these dependencies during package resolution:
- Define the "FirebaseFirestore" trait, enabled by default.
- Gate binary and source Firestore target dependencies on the trait.
- Provide a clear compile-time diagnostic in dummy.m if Firestore is
  included without enabling the trait.
- Gate Combine extensions and integration test targets on the trait.
Disable default Swift package traits for the local Firebase dependency
in FirebaseAITestApp.

Because Firebase AI Logic integration tests do not depend on Cloud
Firestore, opting out of the "FirebaseFirestore" trait prevents Xcode
from downloading heavy underlying dependencies (gRPC, Abseil), speeding
up resolution and validating trait opt-out behavior in Xcode.
@andrewheard andrewheard added this to the Firebase 13 - M187 milestone Sep 23, 2026
@gemini-code-assist

Copy link
Copy Markdown
Contributor
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Revert `#if canImport(Combine) && FirebaseFirestore` back to the
original `#if canImport(Combine) && swift(>=5.0)` in Firestore Combine
extensions.

CocoaPods does not pass SwiftPM trait flags like `-DFirebaseFirestore`,
causing the compiler to exclude the Combine extensions during CocoaPods
builds and failing `sdk.combine.yml` unit tests.

In SwiftPM, `FirebaseFirestoreCombineSwift` is already conditionally
gated on the trait at the target dependency level in `Package.swift`,
so source-level trait guards are unnecessary.
Add changelog entries in FirebaseCore and Firestore for Swift Package
Traits support:
- FirebaseCore: Note general support for Swift Package Traits to prune
  unused dependencies.
- Firestore: Note the `FirebaseFirestore` trait requirement (enabled by
  default) and opt-out capability.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

This pull request introduces support for Swift Package Traits (SE-0450) to the Firebase iOS SDK, specifically targeting the Firestore module. By defining a default-enabled 'FirebaseFirestore' trait, the change allows developers to explicitly opt out of Firestore dependencies if they are not using the product, significantly streamlining the dependency graph for projects using only lightweight Firebase components. The implementation includes robust gating for source and binary targets, updated documentation, and diagnostic checks to ensure a smooth developer experience during configuration.

Highlights

  • Swift Package Traits Integration: Implemented the FirebaseFirestore package trait in Package.swift, allowing developers to opt out of Firestore and its heavy dependencies (gRPC, abseil) to reduce package resolution overhead.
  • Dependency Gating: Conditioned Firestore-related targets and binary dependencies on the presence of the FirebaseFirestore trait using SwiftPM's new conditional dependency syntax.
  • Compile-time Diagnostics: Added a #error check in dummy.m to provide clear feedback if Firestore is linked while the trait is disabled.
  • Test Suite Adaptability: Updated integration tests to conditionally import Firestore, ensuring test suites remain functional regardless of whether the trait is enabled or disabled.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Activity
  • Pull request created by andrewheard.
  • Gemini Code Assist bot provided usage instructions.
  • Author requested a summary via /gemini summary.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for Swift Package Traits (SE-0450) to allow developers to opt out of heavy dependencies like Cloud Firestore (gRPC and Abseil) when they are not needed. It configures the FirebaseFirestore trait in Package.swift and conditionally compiles dependencies and imports based on this trait. The review feedback highlights critical issues in the test files where conditional compilation checks were implemented incorrectly: in Swift, #if canImport(FirebaseFirestore) should be used instead of #if FirebaseFirestore, and in Objective-C, __has_include should be used instead of checking for undefined preprocessor macros, as they do not propagate transitively to the test targets.

Comment thread SwiftPMTests/swift-test/all-imports.swift Outdated
Comment thread SwiftPMTests/objc-import-test/objc-header.m Outdated
Comment thread SwiftPMTests/objc-import-test/objc-header.m Outdated
Comment thread SwiftPMTests/objc-import-test/objc-module.m Outdated
Rename the default package trait from "FirebaseFirestore" to
"FirestoreSupport" across Package.swift, test targets, and error
diagnostics.

Using "FirestoreSupport" clarifies that the compilation condition
(`#if FirestoreSupport`) represents a Swift Package Trait rather
than a module availability check, and aligns with SE-0450 naming
conventions. Also clarify the diagnostic in dummy.m to explicitly
reference SwiftPM.
Rename the default Swift Package Trait from "FirebaseFirestore" to
"Firestore" and refine conditional compilation across the manifest,
test targets, and diagnostics:

- Rename the trait to "Firestore" in Package.swift and update all
  target trait conditions. Because the module is "FirebaseFirestore",
  `#if Firestore` avoids confusion with module availability checks.
- Add an inline comment in `all-imports.swift` documenting that
  `#if Firestore` gates imports based on the SwiftPM trait.
- Rename the C preprocessor macro to `FIRESTORE_TRAIT_ENABLED` in
  target cSettings for both `FirebaseFirestoreTarget` and
  `objc-import-test`, removing dead `defined(Firestore)` checks in
  Objective-C test files (since SwiftPM traits only define compiler
  conditions for swiftc).
- Update the diagnostic in `dummy.m` to explicitly reference SwiftPM
  and the "Firestore" trait.
- Update the entry in `Firestore/CHANGELOG.md` to reference "Firestore".
# Conflicts:
#	FirebaseCore/CHANGELOG.md
@andrewheard

Copy link
Copy Markdown
Contributor Author

Note: The CI failure is likely just due to #16690. Will re-run after merging.

Comment thread FirebaseCore/CHANGELOG.md Outdated
Document how developers can opt out of the "Firestore" trait using
Xcode's Package Dependencies UI and refine the compile-time diagnostic:

- Update FirebaseCore/CHANGELOG.md with instructions to uncheck `default`
  and select only desired traits in the Xcode Traits dropdown (showing
  `None` when all traits are disabled).
- Update Package.swift doc comments to instruct unchecking unwanted traits
  in the Traits dropdown in Xcode (26.4+).
- Refine the diagnostic in dummy.m to point specifically to the "Package
  Dependencies tab" of the Xcode project.
# Conflicts:
#	Firestore/CHANGELOG.md
@andrewheard
andrewheard marked this pull request as ready for review September 23, 2026 22:52
@ncooke3 ncooke3 closed this Sep 23, 2026
@ncooke3 ncooke3 reopened this Sep 23, 2026
@ncooke3

ncooke3 commented Sep 23, 2026

Copy link
Copy Markdown
Member

Re-opening after #16544 merged.

@ncooke3

ncooke3 commented Sep 24, 2026

Copy link
Copy Markdown
Member

Ah, my bad. Main needed to be merged in. I thought merging the other PR and restarting CI was enough. CI looks good now 👍 .

@andrewheard
andrewheard merged commit 54e836b into main Sep 24, 2026
171 of 173 checks passed
@andrewheard
andrewheard deleted the ah/swiftpm-traits branch September 24, 2026 02:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FR]: SPM downloads gRPC/abseil binaries (652 MB) even when Firestore is not used

3 participants