Skip to content

Instantly share code, notes, and snippets.

@michaelevensen
Created August 20, 2019 08:57
Show Gist options
  • Save michaelevensen/c74b79292fa8dba437552f7a9f10a5dc to your computer and use it in GitHub Desktop.
Save michaelevensen/c74b79292fa8dba437552f7a9f10a5dc to your computer and use it in GitHub Desktop.
LocalCollection to be used together with DataSource for Firestore Queries.
//
// Copyright (c) 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import FirebaseFirestore
/// A type that can be initialized from a Firestore document.
public protocol DocumentSerializable {
/// Initializes an instance from a Firestore document. May fail if the
/// document is missing required fields.
init?(document: QueryDocumentSnapshot)
/// Initializes an instance from a Firestore document. May fail if the
/// document does not exist or is missing required fields.
init?(document: DocumentSnapshot)
/// The documentID of the object in Firestore.
var documentID: String { get }
/// The representation of a document-serializable object in Firestore.
var documentData: [String: Any] { get }
}
final class LocalCollection<T: DocumentSerializable> {
private(set) var items: [T]
private(set) var documents: [DocumentSnapshot] = []
let query: Query
private let updateHandler: ([DocumentChange]) -> ()
private var listener: ListenerRegistration? {
didSet {
oldValue?.remove()
}
}
var count: Int {
return self.items.count
}
subscript(index: Int) -> T {
return self.items[index]
}
init(query: Query, updateHandler: @escaping ([DocumentChange]) -> ()) {
self.items = []
self.query = query
self.updateHandler = updateHandler
}
func index(of document: DocumentSnapshot) -> Int? {
for i in 0 ..< documents.count {
if documents[i].documentID == document.documentID {
return i
}
}
return nil
}
func listen() {
guard listener == nil else { return }
listener = query.addSnapshotListener { [unowned self] querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshot results: \(error!)")
return
}
let models = snapshot.documents.map { (document) -> T in
if let model = T(document: document) {
return model
} else {
// handle error
fatalError("Unable to initialize type \(T.self) with dictionary \(document.data())")
}
}
self.items = models
self.documents = snapshot.documents
self.updateHandler(snapshot.documentChanges)
}
}
func stopListening() {
listener?.remove()
listener = nil
}
deinit {
stopListening()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment