Last active
March 27, 2023 22:15
-
-
Save tera-ny/cf723f5857d2bdea36b331f9c1427c3d to your computer and use it in GitHub Desktop.
Swift + Combine + Firestore
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import FirebaseFirestoreSwift | |
import FirebaseFirestore | |
import Combine | |
struct Document<Model: Codable> { | |
let ref: DocumentReference | |
let data: Model | |
static func get(collectionPath: String, id: String) -> Deferred<Future<Document<Model>, Error>> { | |
.init { () -> Future<Document<Model>, Error> in | |
let document = Firestore.firestore().collection(collectionPath).document(id) | |
return get(documentRef: document) | |
} | |
} | |
static func get(documentRef: DocumentReference) -> Deferred<Future<Document<Model>, Error>> { | |
.init { () -> Future<Document<Model>, Error> in | |
get(documentRef: documentRef) | |
} | |
} | |
static func listen(documentRef: DocumentReference) -> Deferred<FirestoreDocumentPublisher<Model>> { | |
.init { () -> FirestoreDocumentPublisher<Model> in | |
listen(documentRef: documentRef) | |
} | |
} | |
static func listen(query: Query) -> Deferred<FirestoreCollectionPublisher<Model>> { | |
.init { () -> FirestoreCollectionPublisher<Model> in | |
listen(query: query) | |
} | |
} | |
private static func get(documentRef: DocumentReference) -> Future<Document<Model>, Error> { | |
.init { observer in | |
documentRef.getDocument { (snapshot, error) in | |
if let error = error { | |
observer(.failure(error)) | |
} else { | |
do { | |
let data = try snapshot!.data(as: Model.self, decoder: Firestore.Decoder())! | |
observer(.success(.init(ref: documentRef, data: data))) | |
} catch { | |
observer(.failure(error)) | |
} | |
} | |
} | |
} | |
} | |
private static func get(query: Query) -> Future<[Document<Model>], Error> { | |
.init { observer in | |
query.getDocuments { (snapshot, error) in | |
if let error = error { | |
observer(.failure(error)) | |
} else { | |
do { | |
let data = try snapshot!.documents.map { document -> Document<Model> in | |
let data = try document.data(as: Model.self, decoder: Firestore.Decoder())! | |
return .init(ref: document.reference, data: data) | |
} | |
observer(.success(data)) | |
} catch { | |
observer(.failure(error)) | |
} | |
} | |
} | |
} | |
} | |
private static func listen(documentRef: DocumentReference) -> FirestoreDocumentPublisher<Model> { | |
return .init(documentRef: documentRef) | |
} | |
private static func listen(query: Query) -> FirestoreCollectionPublisher<Model> { | |
return .init(query: query) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import FirebaseFirestore | |
import Combine | |
struct FirestoreCollectionPublisher<Model: Codable>: Publisher { | |
typealias Output = [Document<Model>] | |
typealias Failure = Error | |
let query: Query | |
init(query: Query) { | |
self.query = query | |
} | |
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input { | |
let subscription = FirestoreSubscription(query: query, subscriber: subscriber) | |
subscriber.receive(subscription: subscription) | |
} | |
} | |
struct FirestoreDocumentPublisher<Model: Codable>: Publisher { | |
typealias Output = Document<Model> | |
typealias Failure = Error | |
let ref: DocumentReference | |
init(documentRef: DocumentReference) { | |
self.ref = documentRef | |
} | |
func receive<S>(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input { | |
let subscription = FirestoreSubscription(documentRef: ref, subscriber: subscriber) | |
subscriber.receive(subscription: subscription) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import FirebaseFirestore | |
import Combine | |
class FirestoreSubscription<S: Subscriber, Model: Codable>: Subscription { | |
private var subscriber: S? | |
private var listener: ListenerRegistration? = nil | |
init(subscriber: S) { | |
self.subscriber = subscriber | |
self.listener = nil | |
} | |
deinit { | |
listener?.remove() | |
} | |
func request(_ demand: Subscribers.Demand) { | |
} | |
func cancel() { | |
subscriber = nil | |
listener?.remove() | |
listener = nil | |
} | |
} | |
extension FirestoreSubscription where S.Input == [Document<Model>], S.Failure == Error { | |
convenience init(query: Query, subscriber: S) { | |
self.init(subscriber: subscriber) | |
self.listener = query.addSnapshotListener(result()) | |
} | |
func result() -> FIRQuerySnapshotBlock { | |
{ [weak self] (snapshot, error) in | |
if let error = error { | |
self?.subscriber?.receive(completion: Subscribers.Completion.failure(error)) | |
} else { | |
do { | |
let data = try snapshot!.documents.map { document -> Document<Model> in | |
let data = try document.data(as: Model.self, decoder: Firestore.Decoder())! | |
return .init(ref: document.reference, data: data) | |
} | |
_ = self?.subscriber?.receive(data) | |
} catch { | |
self?.subscriber?.receive(completion: Subscribers.Completion.failure(error)) | |
} | |
} | |
} | |
} | |
} | |
extension FirestoreSubscription where S.Input == Document<Model>, S.Failure == Error { | |
convenience init(documentRef: DocumentReference, subscriber: S) { | |
self.init(subscriber: subscriber) | |
self.listener = documentRef.addSnapshotListener(result(ref: documentRef)) | |
} | |
func result(ref: DocumentReference) -> FIRDocumentSnapshotBlock { | |
{ [weak self] (snapshot, error) in | |
if let error = error { | |
self?.subscriber?.receive(completion: Subscribers.Completion.failure(error)) | |
} else { | |
do { | |
let data = try snapshot!.data(as: Model.self, decoder: Firestore.Decoder())! | |
_ = self?.subscriber?.receive(.init(ref: ref, data: data)) | |
} catch { | |
self?.subscriber?.receive(completion: Subscribers.Completion.failure(error)) | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class FriendsViewModel: ObservableObject { | |
@Published var friends: [Document<User>] = [] | |
var cancellable: AnyCancellable? = nil | |
@ObservedObject var authStore = AuthStore(auth: Auth.auth()) | |
init() { | |
bind() | |
} | |
func bind() { | |
cancellable = Document<Room>.listen(query: Firestore.firestore().collection("user").whereField("friends", arrayContains: Auth.auth().currentUser!.uid).limit(to: 10)).sink(receiveCompletion: { error in | |
}, receiveValue: { [weak self] friends in | |
print("My friends..",friends.joined(separator: ",")) | |
self?.friends = friends | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment