This document provides essential context and guidelines for Jules, an AI software engineering
agent, to effectively understand, navigate, and make changes within the firebase-ios-sdk
repository. It highlights key aspects of the development environment, coding practices, and
architectural patterns.
To contribute to or develop within the firebase-ios-sdk repository, an understanding of the
following setup is beneficial.
- Xcode: The development environment relies on Xcode 16.2 or above.
- Command-Line Tools:
clang-format: Used for C, C++, and Objective-C code formatting. Version 20 is specifically mentioned.mint: Used to install and run Swift command-line tools, includingswiftformatfor Swift code styling.- Note: The build environment uses
clang-format(version 20) andmint(forswiftformat) for code styling. Ensure any generated or modified code adheres to these styling requirements.
You can develop Firebase libraries using either Swift Package Manager (SPM) or CocoaPods.
Swift Package Manager (SPM) is a primary development workflow. The project is typically opened
via the Package.swift file at the repository root.
- When working with SPM, specific library schemes (e.g.,
FirebaseAuth,FirebaseFirestore) are used to build and develop individual modules. - For test-related tasks using SPM, be aware of the
./scripts/setup_spm_tests.shscript, which configures Xcode to show schemes for testing individual packages. This script may need to be invoked if tests need to be run or modified in an SPM context../scripts/setup_spm_tests.sh
CocoaPods (v1.12.0+) is another key development workflow.
- The
cocoapods-generateplugin is used to create a development workspace from a.podspecfile. - Development workspaces for individual libraries (e.g.,
FirebaseStorage) are generated using a command like:pod gen FirebaseStorage.podspec --local-sources=./ --auto-open --platforms=ios
- Replace
FirebaseStorage.podspecwith the podspec of the library you intend to develop. Podspecs are located in the root directory. - The
--local-sources=./flag tells CocoaPods to use the local checkout of the SDK. --platformscan be set toios,macos, ortvos. Note that for Xcode 10.2+, multi-platform CocoaPods workspaces might have issues, so generating for a single platform is advised byREADME.md.- If the CocoaPods cache is outdated, you might need to run
pod repo updatefirst.
- Replace
- Understanding this command structure is important if tasks involve setting up or modifying a CocoaPods-based development environment for a specific module.
- Mac Catalyst Development:
- Run
pod genas above forios. - In the generated project, check the "Mac" box in the host app's "Build Settings" under Deployment.
- Configure signing for the host app in "Signing & Capabilities."
- In the "Pods" project, add signing to the host app and unit test targets.
- Alternatively, disable code signing by adding a user-defined setting
CODE_SIGNING_REQUIREDwith valueNOto each relevant target's build settings.
- Run
Code consistency is maintained by clang-format (for C-based languages) and swiftformat (for
Swift). Any code generated or modified must adhere to these standards.
- The
./scripts/style.shscript is used to apply formatting. If a task involves code changes, this script (or the underlying tools) should be used to ensure compliance:For example:./scripts/style.sh {path_to_changed_files_or_branch_name}Running./scripts/style.sh FirebaseStorage/Sources/ # OR ./scripts/style.sh my-feature-branch./scripts/style.shwith no arguments will format all eligible files in the repository. - Failures in CI style checks often indicate that code modifications were not formatted correctly with these tools.
Firestore has a self-contained Xcode project. For details, refer to Firestore/README.md.
Thorough testing is essential for maintaining the quality and stability of the Firebase SDK.
- Write Tests:
- When fixing a bug, add a test to prevent regressions.
- When adding a new feature, include tests to validate the new or modified APIs.
- Tests as Documentation: Well-written tests can serve as examples of how to use an API.
- Code Coverage: Aim for good code coverage to ensure all critical paths are tested.
The primary method for running tests is through Xcode, after setting up your development environment using either Swift Package Manager or CocoaPods.
- Enable Test Schemes: If you haven't already, run the script from the repository root:
./scripts/setup_spm_tests.sh
- Run Tests:
- In Xcode, select the scheme corresponding to the library or test suite you want to run
(e.g.,
FirebaseFirestoreTests). - Choose the target platform (run destination) along with the scheme.
- Press
⌘Uor click the "play" arrow in the project navigation bar to run the tests. - Note:
CONTRIBUTING.mdmentions that not all test schemes might be configured to run with SPM.
- In Xcode, select the scheme corresponding to the library or test suite you want to run
(e.g.,
The following are environment variables to modify sources of code that can help with testing specific frameworks or dealing with version mismatches.
FIREBASECI_USE_LATEST_GOOGLEAPPMEASUREMENT: When passed, sets the dependency on the https://github.com/google/GoogleAppMeasurement.git package to use the main branch. This is done to keep the Firebase package's main branch building when it depends on unreleased changes in the GoogleAppMeasurement package's main branch. Use it when you run into version issues.FIREBASECI_USE_LOCAL_FIRESTORE_ZIP: When passed, looks for a localFirebaseFirestoreInternal.xcframeworkframework at the root of the repo to resolve the Firestore target. Used for testing incombination withscripts/check_firestore_symbols.sh.FIREBASE_SOURCE_FIRESTORE: When passed, builds Firestore's large C++ dependencies (abseil, gRPC, BoringSSL) from source rather than, by default, using existing binaries.
To enable an env var within Xcode, quit the current Xcode instance, and open it from the command
line, passing each desired env var with the --env argument.
open --env FIREBASECI_USE_LATEST_GOOGLEAPPMEASUREMENT --env FIREBASE_SOURCE_FIRESTORE \
Package.swiftTo unset the env vars, quit the running Xcode instance.
- Generate Workspace: After generating a workspace using
pod gen ...as described in the "Setup Commands" section. - Run Tests:
- Open the generated
.xcworkspace. - Select the appropriate scheme for the unit tests of the library
(e.g.,
FirebaseStorage-Unit-unit). - Press
⌘Uor click the "play" arrow to run tests.
- Open the generated
- Enable in Xcode:
- Go to
Product → Scheme ➞ Edit Scheme...(or use⌥⌘U). - Select the "Test" action in the sidebar.
- Go to the "Options" tab.
- Check the "Code Coverage" box.
- Go to
- Coverage Report Tool: The repository includes a tool for generating more detailed
coverage reports. Refer to its documentation:
scripts/code_coverage_report/README.md.
Configuration files like GoogleService-Info.plist are necessary for Firebase SDKs to connect to
a backend project. How these are handled in tests varies:
- Unit Tests:
- Unit tests should generally not rely on a live backend or a real
GoogleService-Info.plist. - Where configuration is needed (e.g., for
FirebaseApp.configure()), tests often use mock or fakeGoogleService-Info.plistfiles with dummy values. These are typically included directly in the test target's resources. Be aware of this pattern if tests seem to require Firebase app initialization.
- Unit tests should generally not rely on a live backend or a real
- Integration Tests & Sample Apps:
- Integration tests and sample apps, which do interact with live backends, require valid
GoogleService-Info.plistfiles. - In the CI (Continuous Integration) environment, these files are usually provided securely (e.g., via environment variables, mounted secrets, or dedicated build steps that place the correct plist). As an AI agent, you typically won't manage these CI secrets directly.
- If integration tests fail due to configuration issues, it might relate to how the CI environment provides this plist.
- For local execution of sample apps or some integration tests (if specifically requested
for a task), a valid plist obtained from the Firebase console for a test project would
be needed, as described in the original
README.md. However, direct interaction with the Firebase console to download plists is outside typical AI agent operations unless explicitly guided.
- Integration tests and sample apps, which do interact with live backends, require valid
- Firebase Database:
- Integration tests can run against a local Database Emulator or a production instance.
- Emulator: Run
./scripts/run_database_emulator.sh startbefore executing tests. - Production: Provide a valid
GoogleService-Info.plist(copied toFirebaseDatabase/Tests/Resources/GoogleService-Info.plist) and ensure security rules are public during the test run.
- Firebase Storage:
- For integration tests, follow instructions in
FirebaseStorage/Tests/Integration/StorageIntegration.swift.
- For integration tests, follow instructions in
- Push Notifications (General for Messaging, etc.):
- Cannot be tested on the iOS Simulator; requires a physical device.
- Requires specific App ID provisioning in the Apple Developer portal.
- Upload your APNs Provider Authentication Key or certificate to the Firebase Console.
- Ensure the test device is registered in your Apple Developer account.
Designing and maintaining a consistent and developer-friendly API is critical for the Firebase SDK. Adherence to established guidelines is important for a good user experience and should be followed when tasks involve API modifications.
The primary reference for API design is docs/firebase-api-guidelines.md. Key principles for new
API designs include:
- Swift-First: New APIs should be designed and implemented in Swift. Objective-C APIs require strong justification. Generated Swift interfaces for Objective-C APIs should be manually refined for Swift idioms if necessary.
- Swift Code Samples: Swift code samples should be prioritized in API proposals and documentation.
- Async/Await: New asynchronous APIs should use Swift's
async/await. Callback-based APIs require justification (though callbacks are still appropriate for event handlers).// Preferred public func fetchData() async throws -> Data { /* ... */ } // Pre-Swift Concurrency (No longer preferred for new async APIs) public func fetchData(completion: (Data?, Error?) -> Void) { /* ... */ }
- Sendable Types: New APIs should be
Sendablewhere applicable, to allow safe use in concurrent contexts (e.g., within aTask). - Access Control:
publicaccess level should be used for new Swift APIs rather thanopen.- New Swift classes should generally be
finalto prevent subclassing, unless subclassing is an intended design feature.
- API Availability:
- The
@availableattribute is used to specify platform and version compatibility (e.g.,@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)). - The minimum supported versions for SPM are often tied to Analytics/Crashlytics. For other
APIs, the product's CocoaPods podspec is the reference for versioning in
@available.
- The
- Constants:
- Constants in Swift should be defined within case-less enums (prevents instantiation).
- The "k" prefix for Swift constants should be avoided.
public enum NetworkConstants { public static let httpPostMethod = "POST" }
- Minimize Optionals: While Swift's optionals provide type safety for nullability, their overuse should be avoided to prevent complicating call sites.
- Structs vs. Enums for Extensibility:
- For sets of values that might expand in the future,
structswith static factory methods are preferred overenums. Adding a case to a public Swift enum is a breaking change. - See
docs/firebase-api-guidelines.md(links to PRs #13728, #13976) for examples.
- For sets of values that might expand in the future,
- Avoid
Any,AnyObject, NS-Types:Any,AnyObject, or Objective-C NS-prefixed types (likeNSString,NSDictionary) should not be used in public Swift APIs. Swift native types (String,Dictionary) are preferred, and heterogeneous collections should be modeled with enums or structs for type safety. - Documentation: New APIs must be documented using Swift-flavored Markdown. Xcode's
⌥ ⌘ /shortcut can generate documentation block structure. - Naming Conventions:
- Clarity and expressiveness should be prioritized (e.g.,
fetchMetadata()overfetch()orgetMetadata()). - Consistency with existing Firebase APIs must be maintained.
- Refer to Swift's API Design Guidelines.
- Clarity and expressiveness should be prioritized (e.g.,
- Error Handling:
- APIs that can fail should be marked with
throws. - Public error types conforming to Swift's
Errorprotocol should be defined and thrown.
- APIs that can fail should be marked with
- Objective-C (If Necessary):
- Defining
typedefsin public Objective-C headers should be avoided as they obscure the underlying type in the generated Swift interface. Full types should be used.
- Defining
The document HeadersImports.md provides detailed guidelines. Key points include:
- Public Headers:
- Define the library's public API.
- Located in
FirebaseFoo/Sources/Public/FirebaseFoo/. - Additions are minor version updates; changes/deletions are major.
- Public Umbrella Header:
- A single header including the library's full public API
(e.g.,
FirebaseFoo/Sources/Public/FirebaseFoo/FirebaseFoo.h).
- A single header including the library's full public API
(e.g.,
- Private Headers:
- Located in
FirebaseFoo/Sources/Private/. - Available to other libraries within the
firebase-ios-sdkrepo but NOT part of the public API. - For CocoaPods, these are included in
source_files, notprivate_header_files.
- Located in
- Interop Headers:
- Special private headers for defining interfaces between libraries
(see
Interop/FirebaseComponentSystem.md).
- Special private headers for defining interfaces between libraries
(see
- Private Umbrella Header:
- Includes public API + private APIs for other in-repo libraries. Package manager complexities should be localized here.
- Library Internal Headers:
- Only used by the enclosing library, located among its source files.
- Import Styles:
- Within the same library: Use repo-relative paths
(e.g.,
#import "FirebaseFoo/Sources/Internal/MyInternalHeader.h").- Exception: Public headers importing other public headers from the same library
should use unqualified imports (
#import "AnotherPublicHeaderInFoo.h") to avoid module collisions.
- Exception: Public headers importing other public headers from the same library
should use unqualified imports (
- Private Headers from other libraries: Import the private umbrella header
(e.g.,
#import "FirebaseCore/Extension/FirebaseCoreInternal.h"). - External Dependencies:
#if SWIFT_PACKAGE @import GTMSessionFetcherCore; #else #import <GTMSessionFetcher/GTMSessionFetcher.h> #endif
- Within the same library: Use repo-relative paths
(e.g.,
This structure ensures that APIs are well-defined, discoverable, and maintainable across different build systems and for various Firebase products.
Adhering to best practices ensures code quality, maintainability, and a positive developer experience for both contributors and users of the Firebase SDK.
- Contribution Guidelines: The
CONTRIBUTING.mdfile contains detailed information on the development workflow, pull requests, coding styles, and environment setup, which should be adhered to. - Code of Conduct: All contributions and interactions must align with the Code of Conduct.
- Swift-First Development: New features and APIs should prioritize Swift, as outlined in the API guidelines. Objective-C is used only with strong justification.
- Modularity: The SDK's modular design, with each Firebase product in its own library/pod (e.g., Auth, Firestore, Storage), promotes separation of concerns. Changes should generally be focused within a product's dedicated directory.
- Dependency Management:
- Internal dependencies (between Firebase pods) are managed via version specifications in
.podspecfiles orPackage.swift. - External dependencies are also declared here. The impact of adding new external dependencies requires careful consideration.
- Internal dependencies (between Firebase pods) are managed via version specifications in
- Central FirebaseApp (
FIRApp):FirebaseApp(Swift) /FIRApp(Objective-C) is the central class for initializing and configuring Firebase.- It provides access to project-level configurations from
GoogleService-Info.plist.
- Component System (
FIRComponent):- An internal component system (
FIRComponent,FIRComponentContainer) is used for registering and discovering Firebase services at runtime. - This enables a decoupled architecture. New top-level Firebase pods often require
registration with this system (see
docs/AddNewPod.mdandFIRApp.m).
- An internal component system (
GoogleService-Info.plist(in relation to SDK code):- While handling
GoogleService-Info.plistin tests has specific considerations (see "HandlingGoogleService-Info.plistin Tests"), SDK code itself relies onFirebaseAppto provide project configurations derived from such a file.
- While handling
-ObjCLinker Flag:- Awareness of the
-ObjC"Other Linker Flags" setting is important, as it's necessary for applications using Firebase Analytics (and often transitively, other Firebase SDKs) to correctly link Objective-C categories.
- Awareness of the
- Changelogs:
CHANGELOG.mdfiles (root and product-specific) must be updated with meaningful descriptions of changes for any pull request. - Minimize Breaking Changes: Breaking changes are avoided if possible and require careful consideration, typically aligning with major version releases.
- Platform Support: Different levels of support exist for Apple platforms (macOS, Catalyst,
tvOS, visionOS, watchOS).
@availableand conditional compilation (#if os(iOS)) are used. visionOS and watchOS are community-supported. - Header Hygiene: Guidelines in
HeadersImports.mdmust be followed strictly for compatibility. - Testing: Comprehensive testing (unit, integration) is a core best practice. Tests must cover new code and bug fixes.
Adherence to these practices helps maintain the quality and robustness of the Firebase Apple SDKs.
Recognizing the following common design and implementation patterns, prevalent throughout the Firebase Apple SDKs, facilitates understanding the existing codebase and developing new features consistently.
- Async/Await and Sendable for Swift:
- New asynchronous Swift code predominantly uses
async/awaitfor cleaner, more readable control flow. - Types involved in concurrent operations are increasingly expected to conform to
Sendableto ensure thread safety, especially with Swift 6's stricter concurrency checking.
- New asynchronous Swift code predominantly uses
- Case-less Enums for Constants:
- Swift code uses case-less enums to group related constants, preventing instantiation and providing a clear namespace.
public enum StorageConstants { public static let maxUploadRetryTime: TimeInterval = 600.0 public static let defaultChunkSize: Int64 = 256 * 1024 }
- Structs for Extensible Enum-like Values:
- When a set of predefined values (akin to enum cases) might need to be expanded without
causing breaking changes,
structswith static factory methods are often used. This avoids the issue where adding a new case to a publicenumbreaks clientswitchstatements. (Seedocs/firebase-api-guidelines.mdfor context).
- When a set of predefined values (akin to enum cases) might need to be expanded without
causing breaking changes,
- Protocol Buffers (Protobuf / Nanopb):
- Several modules (e.g., Crashlytics, Firestore, InAppMessaging, Performance, Messaging) utilize Protocol Buffers for data serialization.
- This involves
.protofiles defining data structures and scripts (e.g.,generate_protos.sh) for compilation into Swift or Objective-C (often Nanopb for C/Objective-C). Due to their complexity and impact on build processes, introducing Protobufs to new areas of the SDK should be avoided unless that area already heavily relies on them or it's a specific requirement for interacting with an existing Protobuf-based system.
- API Visibility Layers (Public/Private/Internal):
- A clear distinction is maintained for API visibility:
- Public: APIs intended for end-users, found in
Sources/Public/directories. Changes here are subject to semantic versioning. - Private: APIs intended for use by other Firebase SDKs within this repository
but not for public consumption. Often found in
Sources/Private/orInterop/directories. - Internal/Project: Code and headers used only within the same library/module. These are typically co-located with the module's other source files.
- Public: APIs intended for end-users, found in
- A clear distinction is maintained for API visibility:
- Umbrella Headers:
- Public Umbrella Header: Each library typically has a main public header
(e.g.,
FirebaseFoo.h) that includes all other public headers for that library, simplifying imports for Objective-C users. - Private Umbrella Header: Some libraries may have an internal or extension umbrella
header (e.g.,
FirebaseCoreInternal.h) to expose necessary private APIs to other Firebase libraries.
- Public Umbrella Header: Each library typically has a main public header
(e.g.,
- Module-Specific Prefixes (Objective-C):
- Objective-C class names are typically prefixed to avoid collisions (e.g.,
FIRfor Firebase Core,FSTfor Firestore,FIRCLSfor Crashlytics). This is less of a concern in Swift due to namespacing with module names.
- Objective-C class names are typically prefixed to avoid collisions (e.g.,
- Error Handling Conventions:
- Swift: Adopts the
Errorprotocol, with specific error enums/structs per module. - Objective-C: Uses
NSError**parameters and standard error domains (often module-specific).
- Swift: Adopts the
- Delegate Pattern (Objective-C & Swift):
- Commonly used for callbacks and event notifications, especially in older Objective-C APIs or when multiple distinct events need to be communicated.
- Completion Handlers (Callbacks):
- While
async/awaitis preferred for new Swift asynchronous operations, completion handlers remain prevalent in many existing Objective-C and Swift APIs. These typically take(ResultType?, Error?)or similar parameters.
- While
- Singletons and Shared Instances:
- Many Firebase services offer a shared instance or singleton accessor
(e.g.,
FirebaseApp.app(),Firestore.firestore(),Auth.auth()).
- Many Firebase services offer a shared instance or singleton accessor
(e.g.,
- Resource Bundles:
- SDKs requiring UI components or other resources (e.g., Firebase In-App Messaging) may
package them into
.bundlefiles.
- SDKs requiring UI components or other resources (e.g., Firebase In-App Messaging) may
package them into
- Use of
dispatch_once/ Lazy Initialization for Singletons (Objective-C):- This is a common Objective-C pattern for ensuring singleton instances are initialized
safely and only once. Swift employs
static letproperties for a similar outcome.
- This is a common Objective-C pattern for ensuring singleton instances are initialized
safely and only once. Swift employs
Understanding these patterns is key to navigating the codebase and contributing effectively.
The Firebase Apple SDK integrates several external open-source libraries to provide its functionality. These dependencies are managed as part of the overall SDK build and release process, primarily through CocoaPods and Swift Package Manager configurations.
Examples of such dependencies include (but are not limited to):
- gRPC (for Firestore and other components)
- LevelDB (for Firestore local persistence)
- nanopb (for Protocol Buffer serialization in C/Objective-C contexts)
- abseil-cpp (common C++ libraries)
- GTMSessionFetcher (for networking)
When working on tasks for this repository, do not introduce new external dependencies unless explicitly instructed to do so by the user. Adding and managing dependencies in a large SDK like this involves careful consideration of binary size, licensing, potential conflicts, and long-term maintenance. The existing dependencies are curated and managed by the Firebase team. If a task seems to require functionality that might suggest a new library, discuss alternatives or confirm the need for a new dependency with the user first.
This document (agents.md) is intended as a living guide to assist AI agents (like Jules) in
understanding and contributing to the firebase-ios-sdk repository. As the SDK evolves, new
patterns may emerge, best practices might be updated, or setup procedures could change.
Guideline for Future Tasks: If, during the course of completing new tasks, you identify:
- New common patterns or architectural decisions.
- Changes to the build, test, or setup process.
- Updates to API design philosophies or best practices.
- Any other information that would be beneficial for an AI agent to have context on this repository.
Please consider updating this agents.md file to reflect those new findings. Keeping this
document current will improve the efficiency and accuracy of future AI-assisted development.