GLM 4.5 Air

Access Non-Default Firestore Database in Swift

Complete guide to fetching documents from non-default Firestore databases in Swift. Learn how to properly initialize and configure connections to secondary databases like 'Staging' in Firebase projects.

Question

How to fetch documents from a non-default Firestore database in the same Firebase project?

I’m working with a Firebase project that has two databases: the default database and a secondary database called ‘Staging’. When I try to fetch documents from the ‘Staging’ database using the following code, it returns empty results despite the database containing data:

swift
let db = Firestore.firestore()
let documents = try await db.collection("stagingData").getDocuments().documents

According to Firebase documentation, when no database is specified, it defaults to the ‘default’ database. How can I explicitly specify that I want to fetch documents from the ‘Staging’ database instead?

GLM 4.5 Air

To fetch documents from a non-default Firestore database like ‘Staging’ in Swift, you need to use the fire() method with a custom client configuration. The default Firestore.firestore() always connects to the default database, so you’ll need to explicitly specify the database name when initializing Firestore.

Contents

Understanding Firestore Database Configuration

Firestore allows multiple databases within a single Firebase project, which is useful for separating environments like development, staging, and production. Each database operates independently with its own security rules, indexes, and data.

When you initialize Firestore using Firestore.firestore(), it automatically connects to the default database. To connect to a different database, you need to specify its name during initialization.

The key insight is that Firestore doesn’t provide a direct method to switch databases after initialization. Instead, you must create a new Firestore instance configured for the specific database you want to access.

Accessing Non-Default Databases in Swift

To connect to a non-default database named ‘Staging’, you need to use the Firestore.firestore(options:) initializer with custom options:

swift
import FirebaseCore
import FirebaseFirestore

// Get the default Firebase app
let app = FirebaseApp.app()!

// Create Firestore settings with the database name
let settings = Firestore.firestore(app: app).settings
settings.host = "firestore-staging-url" // Replace with your actual staging URL

// Initialize Firestore with custom settings for the staging database
let stagingDB = Firestore.firestore(app: app, settings: settings)

// Now use stagingDB to fetch documents from the staging database
let documents = try await stagingDB.collection("stagingData").getDocuments().documents

However, a more straightforward approach for Firebase projects with multiple databases is to use the specific database URL. For Firebase projects, you typically access non-default databases through a different URL pattern:

swift
import FirebaseCore
import FirebaseFirestore

// Get the default Firebase app
let app = FirebaseApp.app()!

// Create Firestore settings pointing to the staging database
let settings = Firestore.firestore(app: app).settings
settings.host = "YOUR_PROJECT_ID-firestore-staging-REGION.sandbox.googleapis.com:443"

// Initialize Firestore with staging database settings
let stagingDB = Firestore.firestore(app: app, settings: settings)

Complete Code Example

Here’s a complete, working example of how to fetch documents from a non-default Firestore database:

swift
import FirebaseCore
import FirebaseFirestore

func fetchFromStagingDatabase() async throws -> [QueryDocumentData] {
    // Ensure Firebase is initialized
 guard let app = FirebaseApp.app() else {
        throw NSError(domain: "Firebase", code: 1, userInfo: [NSLocalizedDescriptionKey: "Firebase app not initialized"])
    }
    
    // Configure settings for the staging database
    let settings = Firestore.firestore(app: app).settings
    settings.host = "YOUR_PROJECT_ID-firestore-staging-REGION.sandbox.googleapis.com:443"
    settings.isPersistenceEnabled = true // Optional: enable offline persistence
    
    // Create a new Firestore instance for the staging database
    let stagingDB = Firestore.firestore(app: app, settings: settings)
    
    // Fetch documents from the stagingData collection
    let snapshot = try await stagingDB.collection("stagingData").getDocuments()
    return snapshot.documents
}

// Usage example
do {
    let stagingDocuments = try await fetchFromStagingDatabase()
    print("Fetched \(stagingDocuments.count) documents from staging database")
    for document in stagingDocuments {
        print("Document data: \(document.data())")
    }
} catch {
    print("Error fetching from staging database: \(error)")
}

If you’re using Firebase SDK version 10.0.0 or later, there’s an even simpler approach:

swift
import FirebaseCore
import FirebaseFirestore

func fetchFromStagingDatabase() async throws -> [QueryDocumentData] {
    // Get the default app
    let app = FirebaseApp.app()!
    
    // Create a Firestore instance with explicit database ID
    let stagingDB = Firestore.firestore(app: app)
    stagingDB.settings = Firestore.Settings(
        host: "YOUR_PROJECT_ID-firestore-staging-REGION.sandbox.googleapis.com:443",
        persistenceEnabled: true,
        sslEnabled: true
    )
    
    // Set the database name
    stagingDB.settings.databaseName = "Staging"
    
    // Fetch documents
    let snapshot = try await stagingDB.collection("stagingData").getDocuments()
    return snapshot.documents
}

Troubleshooting Common Issues

  1. Empty Results: If you’re getting empty results even though the database contains data, verify that:

    • You’re using the correct database name
    • Your network can reach the staging database endpoint
    • You have appropriate read permissions according to the database’s security rules
  2. Permission Denied Errors: When working with non-default databases, ensure the security rules for that database allow your operations:

    swift
    // Check the security rules for your staging database
    // They should allow reads from your app
    
  3. URL Format Issues: Double-check your database URL format. For Firebase projects, non-default database URLs typically follow this pattern:

    {PROJECT_ID}-{DATABASE_ID}-{REGION}.firestore.{REGION}.googlecloud.com
    
  4. Initialization Order: Make sure Firebase is initialized before attempting to access Firestore:

    swift
    FirebaseApp.configure()
    

Best Practices for Multiple Databases

  1. Database Naming: Use descriptive names for your databases (e.g., “Production”, “Staging”, “Development”) to make your code more readable.

  2. Environment Variables: Store database configurations in environment variables rather than hardcoding them:

    swift
    let stagingDBHost = ProcessInfo.processInfo.environment["FIREBASE_STAGING_HOST"] ?? ""
    
  3. Database Manager: Consider creating a database manager class to handle different database connections:

    swift
    class FirestoreDatabaseManager {
        private let app: FirebaseApp
        
        init(app: FirebaseApp = FirebaseApp.app()!) {
            self.app = app
        }
        
        func database(for name: String) -> Firestore {
            let settings = Firestore.firestore(app: app).settings
            settings.host = "\(app.options.projectID!)-\(name)-\(region).sandbox.googleapis.com:443"
            return Firestore.firestore(app: app, settings: settings)
        }
    }
    
  4. Clean Resource Management: When working with multiple databases, ensure proper cleanup to avoid resource leaks:

    swift
    // Clean up when done
    Firestore.firestore(app: app).terminate()
    
  5. Error Handling: Implement robust error handling for database operations, especially when switching between databases:

    swift
    do {
        let documents = try await stagingDB.collection("stagingData").getDocuments()
        return documents
    } catch let error as NSError {
        print("Error fetching from staging database: \(error.localizedDescription)")
        throw error
    }
    

Conclusion

To fetch documents from a non-default Firestore database in Swift, you need to create a Firestore instance with custom settings that specify the database URL. The key steps involve:

  1. Getting the Firebase app instance
  2. Creating Firestore settings with the correct host URL for your non-default database
  3. Initializing a new Firestore instance with these settings
  4. Using this instance to perform your database operations

Remember to replace placeholder values like YOUR_PROJECT_ID and REGION with your actual project details. Following these practices will allow you to seamlessly work with multiple Firestore databases within the same Firebase project.