Last active
June 2, 2022 08:15
-
-
Save mono0926/ae6c491862370348ad4b4fcc7b4a5556 to your computer and use it in GitHub Desktop.
FirestoreのRxSwiftとの組み合わせ。Codableも活用。 [追記] FirestoreのデータはCodable非対応のプロパティを持ててしまいそれが含まれると対応難しいのでCodableは捨てた方が良いかもしれない
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
// | |
// Database.swift | |
// Model | |
// | |
// Created by mono on 2017/10/14. | |
// Copyright © 2017 Masayuki Ono All rights reserved. | |
// | |
import Foundation | |
import FirebaseCore | |
import FirebaseFirestore | |
import RxSwift | |
import Lib | |
import Api | |
extension Reactive where Base: Firestore { | |
public func setData<T: DatabaseCollection>(_ type: T.Type, | |
documentRef: DocumentReference, | |
fields: [String: Any]) -> Single<()> { | |
return Single.create { observer in | |
documentRef | |
.setData(fields) { error in | |
if let error = error { | |
logger.error(error) | |
observer(.error(error)) | |
} else { | |
observer(.success(())) | |
} | |
} | |
return Disposables.create() | |
} | |
} | |
public func updateData<T: DatabaseCollection>(_ type: T.Type, | |
documentRef: DocumentReference, | |
fields: [String: Any]) -> Single<()> { | |
return Single.create { observer in | |
documentRef | |
.updateData(fields) { error in | |
if let error = error { | |
logger.error(error) | |
observer(.error(error)) | |
} else { | |
observer(.success(())) | |
} | |
} | |
return Disposables.create() | |
} | |
} | |
public func get<T: DatabaseCollection>(_ type: T.Type, | |
documentRef: DocumentReference) -> Single<T> { | |
return Single.create { observer in | |
documentRef | |
.getDocument { snapshot, error in | |
if let error = error { | |
observer(.error(error)) | |
return | |
} | |
guard let snapshot = snapshot else { | |
observer(.error(ApplicationError.unknown)) | |
return | |
} | |
do { | |
observer(.success(try snapshot.makeResult(id: snapshot.documentID))) | |
} catch { | |
logger.error(error) | |
observer(.error(error)) | |
} | |
} | |
return Disposables.create() | |
} | |
} | |
func get<T: DatabaseCollection>(_ type: T.Type, | |
collectionRef: CollectionReference) -> Single<[T]> { | |
return Single.create { observer in | |
collectionRef | |
.getDocuments { snapshot, error in | |
if let error = error { | |
observer(.error(error)) | |
return | |
} | |
guard let snapshot = snapshot else { | |
observer(.error(ApplicationError.unknown)) | |
return | |
} | |
let results = snapshot.documents.flatMap { snapshot -> T? in | |
do { | |
return try snapshot.makeResult(id: snapshot.documentID) | |
} catch { | |
// TODO: error handling | |
logger.error(error) | |
return nil | |
} | |
} | |
observer(.success(results)) | |
} | |
return Disposables.create() | |
} | |
} | |
public func observe<T: DatabaseCollection>(_ type: T.Type, | |
documentRef: DocumentReference) -> Observable<T> { | |
return Observable.create { observer in | |
documentRef | |
.addSnapshotListener { snapshot, error in | |
if let error = error { | |
observer.on(.error(error)) | |
return | |
} | |
guard let snapshot = snapshot else { | |
observer.on(.error(ApplicationError.unknown)) | |
return | |
} | |
do { | |
observer.on(.next(try snapshot.makeResult(id: snapshot.documentID))) | |
} catch { | |
logger.error(error) | |
observer.on(.error(error)) | |
} | |
} | |
return Disposables.create() | |
} | |
} | |
public func observe<T: DatabaseCollection>(_ type: T.Type, | |
collectionRef: CollectionReference) -> Observable<[T]> { | |
return Observable.create { observer in | |
collectionRef | |
.addSnapshotListener { snapshot, error in | |
if let error = error { | |
observer.on(.error(error)) | |
return | |
} | |
guard let snapshot = snapshot else { | |
observer.on(.error(ApplicationError.unknown)) | |
return | |
} | |
// TODO: ここでは全件返しているが、RxRealmのようにchangesetメソッドも欲しい https://github.com/RxSwiftCommunity/RxRealm | |
let results = snapshot.documents.flatMap { snapshot -> T? in | |
do { | |
return try snapshot.makeResult(id: snapshot.documentID) | |
} catch { | |
// TODO: error handling | |
logger.error(error) | |
return nil | |
} | |
} | |
observer.on(.next(results)) | |
} | |
return Disposables.create() | |
} | |
} | |
} | |
extension DocumentSnapshot { | |
func makeResult<T: DatabaseCollection>(id: String) throws -> T { | |
guard exists else { | |
throw ApplicationError.notFoundEntity(documentId: documentID) | |
} | |
let json = data() | |
logger.debug(json) | |
return try T(id: id, json: json) | |
} | |
} | |
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 Lib | |
import FirebaseFirestore | |
public protocol DatabaseCollection { | |
associatedtype FieldType: Codable | |
static var collectionName: String { get } | |
var id: String { get } | |
var fields: FieldType? { get } | |
init(id: String, fields: FieldType?) | |
init(id: String, json: [String: Any]) throws | |
static func makeCollectionRef() -> CollectionReference | |
static func makeDocumentRef(id: String) -> DocumentReference | |
func makeDocumentRef() -> DocumentReference | |
} | |
extension DatabaseCollection { | |
public init(id: String) { | |
self.init(id: id, fields: nil) | |
} | |
public init(id: String, json: [String: Any]) { | |
// TODO: If performance is prioritized, map by hand | |
do { | |
let data = try JSONSerialization.data(withJSONObject: json) | |
let decoded = try JSONDecoder.ghost.decode(FieldType.self, from: data) | |
self.init(id: id, fields: decoded) | |
} catch { | |
logger.error(error) | |
self.init(id: id) | |
} | |
} | |
public static func makeCollectionRef() -> CollectionReference { return Firestore.firestore().collection(collectionName) } | |
public static func makeDocumentRef(id: String) -> DocumentReference { return Self.makeCollectionRef().document(id) } | |
public func makeDocumentRef() -> DocumentReference { return Self.makeDocumentRef(id: id) } | |
} |
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
public struct User: DatabaseCollection { | |
public static let collectionName = "users" | |
public let id: String | |
public let fields: Fields? | |
public struct Fields: Codable { | |
public let profile: Profile | |
} | |
public struct Profile: Codable { | |
public let name: String | |
public let imageUrl: URL | |
public let birthday: Date | |
public let gender: Gender | |
} | |
public init(id: String, fields: Fields?) { | |
self.id = id | |
self.fields = fields | |
} | |
} |
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
Firestore.firestore().rx | |
.observe(User.self, | |
documentRef: user.makeDocumentRef()) | |
.subscribe { [unowned self] (event: Event<User>) in | |
switch event { | |
case .completed: break | |
case .error(let error): logger.error(error) | |
case .next(let user): | |
self.user = user | |
guard let profile = user.fields?.profile else { return } | |
self.userNameLabel.text = profile.name | |
self.userImageView.set(imageUrl: profile.imageUrl, rounded: true) | |
} | |
} | |
.disposed(by: rx.disposeBag) |
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
func update(user: User, parameters: UsersRequests.ProfileUpdateParameters) -> Single<()> { | |
return self.apiClient.response(UsersRequests.Update(userId: user.id, | |
parameters: parameters)) | |
.flatMap { response in | |
return Firestore.firestore().rx.setData(User.self, | |
documentRef: user.makeDocumentRef(), | |
fields: response.asDictionary(containsId: false)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment