Skip to content

Instantly share code, notes, and snippets.

@ts95
Last active October 22, 2021 14:50
Show Gist options
  • Save ts95/bcf5382e9b43f49ef96e3db2da1c4c0f to your computer and use it in GitHub Desktop.
Save ts95/bcf5382e9b43f49ef96e3db2da1c4c0f to your computer and use it in GitHub Desktop.
import FirebaseFirestore
private struct Property {
let label: String
let value: Any
}
struct FirestoreModelData {
let snapshot: DocumentSnapshot
var documentID: String {
return snapshot.documentID
}
var data: [String : Any] {
return snapshot.data()
}
func value<T>(forKey key: String) throws -> T {
guard let value = data[key] as? T else { throw ModelDataError.typeCastFailed }
return value
}
func value<T: RawRepresentable>(forKey key: String) throws -> T where T.RawValue == String {
guard let value = data[key] as? String else { throw ModelDataError.typeCastFailed }
return T(rawValue: value)!
}
func optionalValue<T>(forKey key: String) -> T? {
return data[key] as? T
}
enum ModelDataError: Error {
case typeCastFailed
}
}
protocol StringRawRepresentable {
var stringRawValue: String { get }
}
extension StringRawRepresentable where Self: RawRepresentable, Self.RawValue == String {
var stringRawValue: String { return rawValue }
}
protocol FirestoreModel {
init?(modelData: FirestoreModelData)
var documentID: String! { get }
var customID: String? { get }
var serialized: [String : Any?] { get }
}
extension FirestoreModel {
var serialized: [String : Any?] {
var data = [String : Any?]()
Mirror(reflecting: self).children.forEach { child in
guard let property = child.label.flatMap({ Property(label: $0, value: child.value) }) else { return }
switch property.value {
case let rawRepresentable as StringRawRepresentable:
data[property.label] = rawRepresentable.stringRawValue
default:
data[property.label] = property.value
}
}
return data
}
var customID: String? { return nil }
}
extension DocumentReference {
func setModel(_ model: FirestoreModel) {
var documentData = [String : Any]()
for (key, value) in model.serialized {
if key == "documentID" || key == model.customID { continue }
switch value {
case let rawRepresentable as StringRawRepresentable:
documentData[key] = rawRepresentable.stringRawValue
default:
documentData[key] = value
}
}
setData(documentData)
}
func getModel<Model: FirestoreModel>(_: Model.Type, completion: @escaping (Model?, Error?) -> Void) {
getDocument { snapshot, error in
if let error = error {
completion(nil, error)
return
}
guard let snapshot = snapshot else {
completion(nil, nil)
return
}
completion(Model(modelData: FirestoreModelData(snapshot: snapshot)), nil)
}
}
}
extension Query {
func getModels<Model: FirestoreModel>(_: Model.Type, completion: @escaping ([Model]?, Error?) -> Void) {
getDocuments { snapshot, error in
if let error = error {
completion(nil, error)
return
}
guard let snapshot = snapshot else {
completion(nil, nil)
return
}
completion(snapshot.documents.flatMap { Model(modelData: FirestoreModelData(snapshot: $0)) }, nil)
}
}
}
@willianpinho
Copy link

How can I use this function?

Is like this:
let book = Book.init(modelData: FirestoreModelData(snapshot: document))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment