import SwiftUI import Firebase import FirebaseFirestore import Combine protocol BaseModel: Identifiable, Codable { var id: String? { get set } var collection: String { get } init() } class BaseViewModel<T: BaseModel>: ObservableObject, Identifiable, Equatable { @Published var model: T var id: String { didSet { self.model.id = id } } var cancellables = [AnyCancellable]() private var db = Firestore.firestore() required init(){ let model = T.init() self.model = model self.id = model.id ?? UUID().uuidString } required init(id: String) { var model = T.init() model.id = id self.model = model self.id = id } init(model: T) { self.model = model self.id = model.id ?? UUID().uuidString } static func ==(lhs: BaseViewModel<T>, rhs: BaseViewModel<T>) -> Bool { lhs.model.id == rhs.model.id } func load(completion: @escaping (Bool) -> Void = {finished in}){ if let id = model.id { self.id = id db.collection(model.collection).document(id).getDocument { docSnapshot, error in guard let doc = docSnapshot else { print("Error fetching document: \(error!)") return } do { guard let data = try doc.data(as: T.self) else { print("Document empty \(type(of: self.model)) with id \(id)") return } self.model = data self.loadSubData {finished in if finished{ completion(true) } } } catch { print(error.localizedDescription) } } } } func loadSubData(completion: @escaping(Bool) -> Void = {finished in}) { fatalError("Must be overridden!") } func loadDataByIDs<T, S>(from list: [String], appender: @escaping (T) -> Void) where T: BaseViewModel<S>, S: BaseModel { for id in list { let viewModel = T.init(id: id) viewModel.load{finished in if finished { appender(viewModel) } } } } func save(){ do { let _ = try db.collection(model.collection).addDocument(from: model) } catch { print(error) } } func update(){ if let id = model.id { do { try db.collection(model.collection).document(id).setData(from: model) } catch { print(error.localizedDescription) } } } func delete(){ if let id = model.id { db.collection(model.collection).document(id).delete() { error in if let error = error { print(error.localizedDescription) } } } } }