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

Add support for Swift Package Traits (SE-0450) in Package.swift by
introducing a default-enabled "Firestore" trait.

Previously, depending on the firebase-ios-sdk Swift package resolved
and downloaded Cloud Firestore and its heavy underlying dependencies
(gRPC, Abseil, and prebuilt binary artifacts), even if an application
only used lightweight Firebase products (such as Auth, Crashlytics, or
Remote Config).

With package traits, developers can now opt out of Cloud Firestore in
their Package.swift (via `traits: []`) or through Xcode 26.4+'s Package
Dependencies UI, pruning heavy dependencies and speeding up package
resolution while preserving full backwards compatibility:

- Define the "Firestore" trait in Package.swift, enabled by default in
  `packageTraits`.
- Gate source and binary Firestore target dependencies (abseil, gRPC,
  FirebaseFirestoreInternalWrapper, FirebaseFirestoreCombineSwift) on
  `.when(traits: ["Firestore"])`.
- Add `FIRESTORE_TRAIT_ENABLED` C preprocessor macro via target cSettings
  on `FirebaseFirestoreTarget` and `objc-import-test` to enable
  conditional compilation for Clang targets when the trait is active.
- Provide a clear compile-time `#error` diagnostic in `dummy.m` if an app
  target links Firestore without enabling the trait.
- Gate conditional imports in integration test targets (`swift-test` and
  `objc-import-test`) so test suites pass both with default traits and
  with `--disable-default-traits`.
- Opt out of default traits in `FirebaseAITestApp` (in project.pbxproj),
  validating Xcode trait opt-out and avoiding Firestore dependencies for
  AI Logic integration tests.
- Add release notes and Xcode opt-out instructions to changelogs in
  `FirebaseCore/CHANGELOG.md` and `Firestore/CHANGELOG.md`.
This commit is contained in:
Andrew Heard authored and GitHub committed 2026-09-23 22:50:48 -04:00
1 parent 1e5ea691ce
commit 54e836b780
8 files changed
+95 -23

No files matched your search

@@ -509,6 +509,8 @@
86E850592DBAFBC3002E8D94 /* XCLocalSwiftPackageReference "../../.." */ = {
isa = XCLocalSwiftPackageReference;
relativePath = ../../..;
traits = (
);
};
/* End XCLocalSwiftPackageReference section */
+5
View File
@@ -1,4 +1,9 @@
# Firebase 13.0.0
- [feature] Added support for Swift Package Traits (SE-0450) to allow developers
to opt out of unused features and prune heavy dependencies. To opt out of
Firestore in Xcode: select your project -> **Package Dependencies**, and in
the **Traits** column for Firebase, uncheck `default` and check only the ones
you want, if any (it will show `None` if no traits are enabled). (#16684)
- [changed] **Breaking change**: Firebase's minimum supported versions have
updated for the following platforms:
- | Platform | Firebase 13 |
+4
View File
@@ -7,6 +7,10 @@
APIs.
- [feature] Adds support for the following new types: `MinKey`, `MaxKey`, `RegexValue`,
`Int32Value`, `Decimal128Value`, `BSONObjectId`, `BSONTimestamp`, and `BSONBinaryData`. (#16265)
- [changed] Added the `Firestore` package trait to `Package.swift`.
Cloud Firestore support now requires the trait to be enabled (it is enabled
by default). Developers can opt out of the trait to avoid downloading
Firestore's underlying dependencies (such as gRPC and Abseil). (#16684)
# 12.19.0
- [feature] Added support for `minimum` and `maximum` FieldValue operations [#16159].
+68 -22
View File
@@ -20,12 +20,42 @@ import PackageDescription
let firebaseVersion = "13.0.0"
/// If you don't use Firestore in your app, you can disable the "Firestore" trait to reduce
/// the number of dependencies fetched by SwiftPM during package resolution.
let firestoreTrait = Trait(
name: "Firestore",
description: "Enables Firestore support and its underlying dependencies (gRPC, Abseil)."
)
/// Package traits allow developers to opt out of features, including their dependencies.
///
/// In a `Package.swift`:
/// ```swift
/// .package(
/// url: "https://github.com/firebase/firebase-ios-sdk.git",
/// from: "13.0.0",
/// traits: [
/// // Leave empty (or list only non-Firestore traits) to opt out.
/// ]
/// )
/// ```
///
/// In Xcode (26.4+):
/// - Select your project -> Package Dependencies -> in the Traits dropdown for Firebase,
/// uncheck any unwanted traits.
let packageTraits = Set<Trait>([
firestoreTrait,
// All traits are enabled by default.
.default(enabledTraits: [firestoreTrait.name]),
])
let shouldUseSourceFirestore = Context.environment["FIREBASE_SOURCE_FIRESTORE"] != nil
let package = Package(
name: "Firebase",
platforms: [.iOS(.v15), .macCatalyst(.v15), .macOS(.v11), .tvOS(.v15), .watchOS(.v8)],
products: packageProducts(),
traits: packageTraits,
dependencies: packageDependencies(),
targets: packageTargets(),
cxxLanguageStandard: CXXLanguageStandard.gnucxx17
@@ -533,7 +563,7 @@ func packageTargets() -> [Target] {
.target(
name: "FirebaseFirestoreCombineSwift",
dependencies: [
"FirebaseFirestoreTarget",
.target(name: "FirebaseFirestoreTarget", condition: .when(traits: ["Firestore"])),
],
path: "FirebaseCombineSwift/Sources/Firestore"
),
@@ -1151,13 +1181,15 @@ func packageTargets() -> [Target] {
.target(name: "FirebaseAppDistribution",
condition: .when(platforms: [.iOS])),
"FirebaseAuthCombineSwift",
"FirebaseFirestoreCombineSwift",
.target(name: "FirebaseFirestoreCombineSwift",
condition: .when(traits: ["Firestore"])),
"FirebaseFunctionsCombineSwift",
"FirebaseStorageCombineSwift",
"FirebaseCrashlytics",
"FirebaseCore",
"FirebaseDatabase",
"FirebaseFirestoreTarget",
.target(name: "FirebaseFirestoreTarget",
condition: .when(traits: ["Firestore"])),
"FirebaseFunctions",
.target(name: "FirebaseInAppMessaging",
condition: .when(platforms: [.iOS, .tvOS])),
@@ -1198,7 +1230,8 @@ func packageTargets() -> [Target] {
"FirebaseCrashlytics",
"FirebaseCore",
"FirebaseDatabase",
"FirebaseFirestoreTarget",
.target(name: "FirebaseFirestoreTarget",
condition: .when(traits: ["Firestore"])),
"FirebaseFunctions",
.target(name: "FirebaseInAppMessaging",
condition: .when(platforms: [.iOS, .tvOS])),
@@ -1209,7 +1242,8 @@ func packageTargets() -> [Target] {
"FirebaseRemoteConfig",
"FirebaseStorage",
],
path: "SwiftPMTests/objc-import-test"
path: "SwiftPMTests/objc-import-test",
cSettings: [.define("FIRESTORE_TRAIT_ENABLED", .when(traits: ["Firestore"]))]
),
.testTarget(
name: "version-test",
@@ -1447,24 +1481,30 @@ func firestoreWrapperTarget() -> Target {
if shouldUseSourceFirestore {
return .target(
name: "FirebaseFirestoreTarget",
dependencies: [.target(name: "FirebaseFirestore",
condition: .when(platforms: [
.iOS,
.tvOS,
.macOS,
.visionOS,
.macCatalyst,
]))],
path: "SwiftPM-PlatformExclude/FirebaseFirestoreWrap"
dependencies: [
.target(
name: "FirebaseFirestore",
condition: .when(
platforms: [.iOS, .tvOS, .macOS, .visionOS, .macCatalyst],
traits: ["Firestore"]
)
),
],
path: "SwiftPM-PlatformExclude/FirebaseFirestoreWrap",
cSettings: [.define("FIRESTORE_TRAIT_ENABLED", .when(traits: ["Firestore"]))]
)
}
return .target(
name: "FirebaseFirestoreTarget",
dependencies: [.target(name: "FirebaseFirestore",
condition: .when(platforms: [.iOS, .tvOS, .macOS, .macCatalyst]))],
condition: .when(platforms: [.iOS, .tvOS, .macOS, .macCatalyst],
traits: ["Firestore"]))],
path: "SwiftPM-PlatformExclude/FirebaseFirestoreWrap",
cSettings: [.define("FIREBASE_BINARY_FIRESTORE", to: "1")]
cSettings: [
.define("FIREBASE_BINARY_FIRESTORE", to: "1"),
.define("FIRESTORE_TRAIT_ENABLED", .when(traits: ["Firestore"])),
]
)
}
@@ -1478,8 +1518,10 @@ func firestoreTargets() -> [Target] {
"FirebaseCore",
"leveldb",
.product(name: "nanopb", package: "nanopb"),
.product(name: "abseil", package: "abseil-cpp-SwiftPM"),
.product(name: "gRPC-cpp", package: "grpc-ios"),
.product(name: "abseil", package: "abseil-cpp-SwiftPM",
condition: .when(traits: ["Firestore"])),
.product(name: "gRPC-cpp", package: "grpc-ios",
condition: .when(traits: ["Firestore"])),
],
path: "Firestore",
exclude: [
@@ -1600,17 +1642,20 @@ func firestoreTargets() -> [Target] {
dependencies: [
.target(
name: "FirebaseFirestoreInternalWrapper",
condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS])
condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS],
traits: ["Firestore"])
),
.product(
name: "abseil",
package: "abseil-cpp-binary",
condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS])
condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS],
traits: ["Firestore"])
),
.product(
name: "gRPC-C++",
package: "grpc-binary",
condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS])
condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS],
traits: ["Firestore"])
),
.product(name: "nanopb", package: "nanopb"),
"FirebaseAppCheckInterop",
@@ -1635,7 +1680,8 @@ func firestoreTargets() -> [Target] {
name: "FirebaseFirestoreInternalWrapper",
dependencies: [.target(
name: "FirebaseFirestoreInternal",
condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS])
condition: .when(platforms: [.iOS, .macCatalyst, .tvOS, .macOS],
traits: ["Firestore"])
)],
path: "FirebaseFirestoreInternal",
publicHeadersPath: "."
@@ -17,6 +17,13 @@
#warning "Firebase Firestore does not support watchOS"
#endif
#if !defined(FIRESTORE_TRAIT_ENABLED)
#error "FirebaseFirestore was included in your project dependencies, but the \
'Firestore' SwiftPM package trait is disabled. To use Firestore, please \
enable the 'Firestore' trait (or default traits) in your Package.swift \
or in the Package Dependencies tab of your Xcode project."
#endif // !defined(FIRESTORE_TRAIT_ENABLED)
#if TARGET_OS_VISION && FIREBASE_BINARY_FIRESTORE
#error "Firebase Firestore's binary SPM distribution does not support \
visionOS. To enable the source distribution, quit Xcode and open the desired \
@@ -21,7 +21,9 @@
#import "FirebaseCore/FirebaseCore.h"
#import "FirebaseCrashlytics/FirebaseCrashlytics.h"
#import "FirebaseDatabase/FirebaseDatabase.h"
#if defined(FIRESTORE_TRAIT_ENABLED)
#import "FirebaseFirestore/FirebaseFirestore.h"
#endif // defined(FIRESTORE_TRAIT_ENABLED)
#import "FirebaseInstallations/FirebaseInstallations.h"
#import "FirebaseMessaging/FirebaseMessaging.h"
#if (TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || TARGET_OS_TV
@@ -39,7 +41,9 @@
#import <FirebaseCore/FirebaseCore.h>
#import <FirebaseCrashlytics/FirebaseCrashlytics.h>
#import <FirebaseDatabase/FirebaseDatabase.h>
#if defined(FIRESTORE_TRAIT_ENABLED)
#import <FirebaseFirestore/FirebaseFirestore.h>
#endif // defined(FIRESTORE_TRAIT_ENABLED)
#if TARGET_OS_IOS || TARGET_OS_TV
#import <FirebaseInAppMessaging/FirebaseInAppMessaging.h>
#endif
@@ -22,7 +22,9 @@
@import FirebaseCrashlytics;
@import FirebaseCore;
@import FirebaseDatabase;
#if defined(FIRESTORE_TRAIT_ENABLED)
@import FirebaseFirestore;
#endif // defined(FIRESTORE_TRAIT_ENABLED)
@import FirebaseFunctions;
@import FirebaseInstallations;
@import FirebaseMessaging;
+3 -1
View File
@@ -23,7 +23,9 @@ import Foundation
#endif
import FirebaseCrashlytics
import FirebaseDatabase
import FirebaseFirestore
#if Firestore // Gated by the "Firestore" SwiftPM package trait.
import FirebaseFirestore
#endif // Firestore
import FirebaseFunctions
import FirebaseInstallations
import FirebaseMessaging