Skip to content

Latest commit

 

History

History
239 lines (175 loc) · 5.07 KB

FirestoreClient.md

File metadata and controls

239 lines (175 loc) · 5.07 KB

FirestoreClient

FirestoreClient is a client that helps me use a firestore interface with ease.

Model

First of all, create Model layer inheriting FirestoreModel protocol.

import EasyFirebaseSwift
import FirebaseFirestoreSwift
import FirebaseFirestore

// Note: FirestoreModel inherits `Codable` Protocol
struct Model: FirestoreModel {

    // - MARK : Necessary

    // collectionName corresponding to Firestore's CollectionName.
    static var collectionName: String = "models"

    // NOTE: To use PropertyWrapper such as @DocumentID, @ServerTimestamp, please import FirebaseFirestoreSwift.
    @DocumentID
    var ref: DocumentReference?

    @ServerTimestamp
    var createdAt: Timestamp?

    @ServerTimestamp
    var updatedAt: Timestamp?

    // - MARK : Custom property

    var message: String
}

Create

To create a new document on Firestore, use create method.

Please pay attention that All of createdAt, updatedAt and ref are assigned nil value.

let client = FirestoreClient()

// Note: new model must guarantee `createdAt`, `updatedAt` and `ref` are nil.
let model = Model(
    ref: nil,
    createdAt: nil,
    updatedAt: nil,
    message: "Test"
)

client.create(model) { reference in
    model.ref = reference
} failure: { error in
    print(error)
}

Update

Using update method, we can update an existing model.

Like create method, we have to guarantee model's metadata (createdAt, updatedAt, ref) are not nil value.

let client = FirestoreClient()

// In this case, we fetch an exsisting data from Firestore.
client.get(uid: "example_1234567890") { model in
    var model = model
    model.message = "Update Test"

    // Note: updated model must guarantee `createdAt`, `updatedAt` and `ref` are NOT nil.
    client.update(model) { reference in
        model.ref = reference
    } failure: { error in
        print(error)
    }
} failure: { error in
    print(error)
}

Read

If we want to get snapshots once, we should use get method, on the other hand, if we want to fetch the latest data whenever database is updated, we should use listen method.

Get (fetch once)

get is used like the following.

Note: It is necessary to specify the type of reponse model by giving concrete type(not existential type) at closure parameter like the following.

Collection (Multiple Documents)

client.get(
    filter: [],
    order: [],
    limit: nil
) { (models: [Model]) in // Here, we need to specify concrete type of responsed model.
    for model in models {
        print(model.message)
    }
} failure: { error in
    print(error)
}

Single Document

client.get(uid: "1234567890") { (model: Model) in
    print(model.message)
} failure: { error in
    print(error)
}

Listen(Snapshot)

We can achieve both single observation and query observation of a certain collection at the same time!

listen also specifies which model we want by giving concrete type at closure parameter like the following.

Collection (Multiple Documents)

client.listen(
    filter: [],
    order: [],
    limit: nil
) { (models: [Model]) in // Here, we need to specify concrete type of responsed model.
    for model in models {
        print(model.message)
    }
} failure: { error in
    print(error)
}

Single Document

client.listen(uid: "1234567890") { (model: Model) in
    print(model.message)
} failure: { error in
    print(error)
}

Combine (Experimental)

Supporting Combine!

if you want to check the practical usage, please see this file.

Combine.Create

// Create
model.publisher(for: .create).sink { error in
    print(error)
} receiveValue: { }
.store(in: &cancellables)

Combine.Get

// Get
let ref = Firestore.firestore().collection("models").document("sample")
Model.publisher(for: .get(ref: ref)).sink { completion in
    switch completion {
    case .failure(let error):
        print(error)
    case .finished:
        break
    }
} receiveValue: { model in
    print(model.message)
}
.store(in: &cancellables)

Swift Concurrency (async/await) (Experimental)

We are supporting Swift Concurrency after v1.5.0 !!

if you want to check the practical usage, please see this file.

Filter

It is possible to filter documents.

Core interface to filter documents is FirestoreQueryFilter.

In practice, I have prepared two classes FirestoreEqualFilter and FirestoreRangeFilter.

Please see the following code as an example useCase for FirestoreEqualFilter.

// Create Filter
let equalFilter = FirestoreEqualFilter(
    fieldPath: "message",
    value: "Update Text"
)

// Apply Filter
client.listen(
    filter: [equalFilter],
    order: [],
    limit: nil
)
{ (models: [Model]) in
    // Ensure all response pass the filtering.
    let messageChecking = models.allSatisfy { model in
        model.message == "Update Text"
    }
    // Do All models have `Update Text`? → YES
    assert(messageChecking)
} failure: { error in
    print(error)
}

SubCollection

🚧 WIP

CollectionGroup

🚧 WIP