diff --git a/FirebaseAI/Tests/TestApp/FirebaseAITestApp.xcodeproj/project.pbxproj b/FirebaseAI/Tests/TestApp/FirebaseAITestApp.xcodeproj/project.pbxproj index 5455d5c41e..f75105d2de 100644 --- a/FirebaseAI/Tests/TestApp/FirebaseAITestApp.xcodeproj/project.pbxproj +++ b/FirebaseAI/Tests/TestApp/FirebaseAITestApp.xcodeproj/project.pbxproj @@ -509,6 +509,8 @@ 86E850592DBAFBC3002E8D94 /* XCLocalSwiftPackageReference "../../.." */ = { isa = XCLocalSwiftPackageReference; relativePath = ../../..; + traits = ( + ); }; /* End XCLocalSwiftPackageReference section */ diff --git a/FirebaseCore/CHANGELOG.md b/FirebaseCore/CHANGELOG.md index a087484f82..1951a9291f 100644 --- a/FirebaseCore/CHANGELOG.md +++ b/FirebaseCore/CHANGELOG.md @@ -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 | diff --git a/Firestore/CHANGELOG.md b/Firestore/CHANGELOG.md index 1a1e206022..ff26de6527 100644 --- a/Firestore/CHANGELOG.md +++ b/Firestore/CHANGELOG.md @@ -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]. diff --git a/Package.swift b/Package.swift index 47dbf5c1df..316e28928b 100644 --- a/Package.swift +++ b/Package.swift @@ -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([ + 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: "." diff --git a/SwiftPM-PlatformExclude/FirebaseFirestoreWrap/dummy.m b/SwiftPM-PlatformExclude/FirebaseFirestoreWrap/dummy.m index 4b3c19101c..0a83496982 100644 --- a/SwiftPM-PlatformExclude/FirebaseFirestoreWrap/dummy.m +++ b/SwiftPM-PlatformExclude/FirebaseFirestoreWrap/dummy.m @@ -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 \ diff --git a/SwiftPMTests/objc-import-test/objc-header.m b/SwiftPMTests/objc-import-test/objc-header.m index 1e994c3bbd..7127406673 100644 --- a/SwiftPMTests/objc-import-test/objc-header.m +++ b/SwiftPMTests/objc-import-test/objc-header.m @@ -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 #import #import +#if defined(FIRESTORE_TRAIT_ENABLED) #import +#endif // defined(FIRESTORE_TRAIT_ENABLED) #if TARGET_OS_IOS || TARGET_OS_TV #import #endif diff --git a/SwiftPMTests/objc-import-test/objc-module.m b/SwiftPMTests/objc-import-test/objc-module.m index 02ce7ce653..2801f24ace 100644 --- a/SwiftPMTests/objc-import-test/objc-module.m +++ b/SwiftPMTests/objc-import-test/objc-module.m @@ -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; diff --git a/SwiftPMTests/swift-test/all-imports.swift b/SwiftPMTests/swift-test/all-imports.swift index f7548373f5..9dec74fae7 100644 --- a/SwiftPMTests/swift-test/all-imports.swift +++ b/SwiftPMTests/swift-test/all-imports.swift @@ -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