← Files FirebaseARCHIVED FILE
skills/firebase-firestore/references/enterprise/ios_setup.md
5.83 KB · Oct 5, 2026 · 18:17 UTC
# Firestore Enterprise Native Mode on iOS
This guide walks you through using the Cloud Firestore SDK in your iOS app using
Swift.
# ⛔️ CRITICAL RULE: NO FirebaseFirestoreSwift ⛔️
UNDER NO CIRCUMSTANCES should you import, link against, or configure a project
to use `FirebaseFirestoreSwift`.
As of Firebase SDK v11+, all Swift-specific features (including `@DocumentID`,
`@ServerTimestamp`, and `Codable` support) have been fully merged into the main
`FirebaseFirestore` module.
- NEVER add `.external(name: "FirebaseFirestoreSwift")` or similar to SPM or
Xcode configurations.
- NEVER write `import FirebaseFirestoreSwift` in any Swift file.
- ONLY use `import FirebaseFirestore`.
This is a zero-tolerance constraint. Using `FirebaseFirestoreSwift` is
fundamentally incorrect and unacceptable.
# ⛔️ CRITICAL RULE: NO INLINE INITIALIZATION ⛔️
NEVER write `let db = Firestore.firestore()` or `Firestore.firestore(database:)`
as an inline class or struct property if there is ANY chance the object is
instantiated before `FirebaseApp.configure()` executes in the app root.
- **FATAL CRASH:**
`@Observable class DataManager { let db = Firestore.firestore() }` initialized
as a `@State` in the App root.
- **SAFE PATTERN:** Initialize `Firestore.firestore()` lazily
(`lazy var db = Firestore.firestore()`) OR explicitly initialize the manager
*after* `FirebaseApp.configure()` finishes.
## 1. Import and Initialize
Ensure you have installed the `FirebaseFirestore` SDK. Use the
`xcode-project-setup` skill to automate adding the SPM dependency to the Xcode
project.
```swift
import FirebaseFirestore
```
Initialize an instance of Cloud Firestore. **CRITICAL**: Enterprise databases
require a custom database ID and cannot use the `(default)` instance.
```swift
// Replace "your-enterprise-database-id" with your actual database ID
let db = Firestore.firestore(database: "your-enterprise-database-id")
```
## 2. Type-Safe Data Models (Codable)
To leverage modern Swift data modeling, define your data as `Codable` structs.
The main `FirebaseFirestore` module automatically supports mapping these types.
```swift
struct User: Codable {
@DocumentID var id: String?
var firstName: String
var lastName: String
var born: Int
}
```
## 3. Basic CRUD Operations
The operations are identical to standard Firestore, but ensure you use the `db`
instance initialized with your Enterprise database ID.
### Writing Data (Modern Concurrency & Codable)
```swift
let user = User(firstName: "Ada", lastName: "Lovelace", born: 1815)
do {
// Add a new document with a generated ID using Codable
let ref = try db.collection("users").addDocument(from: user)
print("Document added with ID: \(ref.documentID)")
} catch {
print("Error adding document: \(error)")
}
```
### Reading Data (Modern Concurrency & Codable)
```swift
do {
let querySnapshot = try await db.collection("users").getDocuments()
// Map documents to the User struct automatically
let users = querySnapshot.documents.compactMap { document in
try? document.data(as: User.self)
}
for user in users {
print("Found user: \(user.firstName) \(user.lastName)")
}
} catch {
print("Error getting documents: \(error)")
}
```
## 4. Pipeline Queries
Firestore Enterprise supports Pipeline operations for complex queries.
### Initialization
```swift
let pipeline = db.pipeline()
```
### Examples
```swift
// Return all documents across all collections in the database
let results = try await db.pipeline().database().execute()
// Filtered query
let results = try await db.pipeline()
.collection("cities")
.where(Field("name").equal(Constant("Toronto")))
.execute()
// Compound query
let results = try await db.pipeline()
.collection("books")
.where(Field("rating").equal(5) && Field("published").lessThan(1900))
.execute()
```
## 5. Realtime Listeners in SwiftUI (Lifecycle Best Practices)
When implementing Firestore realtime listeners (`addSnapshotListener`) within a
SwiftUI application, you **MUST** tie the listener lifecycle to the view's
identity using `.task(id:)`, NOT `.onDisappear`.
### ⛔️ UNSAFE PATTERN (.onDisappear)
Presenting a `.sheet` or `.fullScreenCover` can trigger the underlying view's
`onDisappear` method. If you stop your listener here, the feed will stop
updating while the sheet is open, and won't resume when it's dismissed.
### ✅ SAFE PATTERN (.task with deinit)
Because `addSnapshotListener` is a synchronous call, placing it inside a `.task`
means the task completes immediately. This breaks SwiftUI's automatic
cancellation mechanism.
To safely manage traditional Firebase listeners in SwiftUI, you must use
**`deinit`** to handle memory cleanup when the view is destroyed, and
**`.task(id:)`** to handle data identity changes while the view is active.
```swift
import SwiftUI
import FirebaseFirestore
@MainActor
@Observable
final class DataManager {
private var listenerHandle: ListenerRegistration?
var data: [String] = []
func startListening(for userId: String) {
// 1. Clean up any existing listener to prevent duplicates if the ID changes
stopListening()
// 2. Start the regular listener and capture the handle
// Note: Using the global default instance here, make sure to use your enterprise instance if applicable
// For enterprise, you might need to pass the db instance or use a shared manager.
listenerHandle = Firestore.firestore(database: "your-enterprise-database-id").collection("users").document(userId).addSnapshotListener { snapshot, error in
// Handle updates
}
}
func stopListening() {
listenerHandle?.remove()
listenerHandle = nil
}
// 3. Guarantee cleanup when the View is destroyed and this object is deallocated
isolated deinit {
stopListening()
}
}
```
SHA-256: 9e6fc564e0e716aebf451845b1fe5bd64a7ad5348c9d1173c2290c0391268247