RxCloudFirestore is an RxSwift wrapper around iOS Firestore API that works seamlessly with Codable Protocol in Swift 4.0.
This library is still work-in-progress. Any suggestions or issue reports are welcomed!
- Get a single event for a single document or a collection of documents.
- Observe a stream of events for a single document or a collection of documents.
- Get a single event for updating an item.
- Get a single event for setting an item.
- Swift 4.0
- Google Firestore (https://firebase.google.com/docs/firestore/)
- RxSwift (https://github.com/ReactiveX/RxSwift)
Let's start off by defining a simple data structure. Assuming we are designing a database for a collection of schools that have subcollections of courses:
We will use <>{...} to annotate a collection type that follows a collection name.
schools <> {
name: "",
courses <> {
name: "",
startDate: 0, // Timestamp
endDate: 0, // Timestamp
}
}
Conform to both FirestoreCollection & SnapshotCodable Protocols, and implement 2 things as below:
- Supply your collection name as defined in your Firestore console (If it is a subcollection, just use the name of the subcollection and ignore the whole path. We will get into building full subcollection path later).
- Implement the key as constant because it is internally handled by Firestore and should be read-only.
struct School: FirestoreCollection, SnapshotCodable {
static let collectionName = "schools" // 1
let key: String // 2
var name: String
}
struct Course: FirestoreCollection, SnapshotCodable {
static let collectionName = "courses" // 1
let key: String // 2
var name: String
var startDate: Int
var endDate: Int
}
GoogleFirestore.firestore().rx
.observe(School.collection(), School.self)
.subscribe(onNext: { allSchools in
allSchools.forEach({ school in
print(school)
})
})
.disposed(by: disposeBag)
If you want to combine the path with a query, just append Firestore query functions as usual:
GoogleFirestore.firestore().rx
.observe(School.collection().whereField("name", isEqualTo: "MIT"), School.self)
.subscribe(onNext: { allSchools in
allSchools.forEach({ school in
print(school)
})
})
.disposed(by: disposeBag)
let key = "FirebaseID"
GoogleFirestore.firestore().rx
.observe(School.document(key: key), School.self)
.subscribe(onNext: {school in
print(school)
})
.disposed(by: disposeBag)
Chain your collection path and document path together like an alternating pattern to "navigate" through subcollections: Collection(odd) -> Document(even) -> Collection(odd)
// Return a specific document from a subcollection from its parent document
GoogleFirestore.firestore().rx
.observe(
School.collection()
.document(key: "SchoolID")
.subcollection(Course.self)
.document(key: "CourseID"),
Course.self
)
.subscribe(onNext: {course in
print(course)
})
.disposed(by: disposeBag)
// Return an entire subcollection from its parent document
GoogleFirestore.firestore().rx
.observe(
School.collection()
.document(key: "SchoolID")
.subcollection(Course.self),
Course.self
)
.subscribe(onNext: { allCourses in
allCourses.forEach({ course in
print(course)
})
})
.disposed(by: disposeBag)
- All Firestore callbacks happen on their own threads (not the main thread). So if your observer needs to perform tasks on the main thread, make sure you observeOn(MainScheduler.instance)
- If your model has a child object, that child also needs to conform to SnapshotCodable (but does not need to conform to FirestoreCollection since it is not a collection type).