In my data model class I add four snapshot listeners to listen to document changes. I noticed that I am duplicating the code for each collection and only changing where the data is being stored and the data type when I add snapshot listener. How can I eliminate this duplication in an elegant way?
class Model: ObservableObject {
@Published var tasks: [Task] = []
@Published var notes: [Note] = []
@Published var projects: [Project] = []
@Published var notebooks: [Notebook] = []
private var db = Firestore.firestore()
init() {
let _ = db.collection("tasks").whereField("userID", isEqualTo: Auth.auth().currentUser?.uid ?? "")
.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("No documents in 'tasks' collection")
return
}
self.tasks = documents.compactMap { queryDocumentSnapshot in
try? queryDocumentSnapshot.data(as: Task.self)
}
}
let _ = db.collection("projects").whereField("userID", isEqualTo: Auth.auth().currentUser?.uid ?? "")
.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("No documents in 'projects' collection")
return
}
self.projects = documents.compactMap { queryDocumentSnapshot in
try? queryDocumentSnapshot.data(as: Project.self)
}
}
let _ = db.collection("notes").whereField("userID", isEqualTo: Auth.auth().currentUser?.uid ?? "")
.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("No documents in 'notes' collection")
return
}
self.notes = documents.compactMap { queryDocumentSnapshot in
try? queryDocumentSnapshot.data(as: Note.self)
}
}
let _ = db.collection("notebooks").whereField("userID", isEqualTo: Auth.auth().currentUser?.uid ?? "")
.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("No documents in 'notebooks' collection")
return
}
self.notebooks = documents.compactMap { queryDocumentSnapshot in
try? queryDocumentSnapshot.data(as: Notebook.self)
}
}
}
}
I thought about creating an enum of my collections on the database however I am unclear as to how I would determine how to map to the correct data type and retrieve the correct array for storage without a verbose if statement.
enum Collection: String, CaseIterable {
case tasks = "tasks",
case notes = "notes",
case projects = "projects",
case notebooks = "notebooks"
}
for collection in Collection.allCases {
// Add snapshot listener
}
CodePudding user response:
you could try something like this approach (untested, I don't have Firestore or your models),
using a completion handler
. Adjust the example code to suit your needs.
enum Collection: String, CaseIterable {
case tasks = "tasks"
case notes = "notes"
case projects = "projects"
case notebooks = "notebooks"
}
class Model: ObservableObject {
@Published var tasks: [Task] = []
@Published var notes: [Note] = []
@Published var projects: [Project] = []
@Published var notebooks: [Notebook] = []
private var db = Firestore.firestore()
func collect<T>(col: Collection, handler: @escaping ([T]) -> Void) { // <-- here
let _ = db.collection(col.rawValue).whereField("userID", isEqualTo: Auth.auth().currentUser?.uid ?? "")
.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("No documents in collection: \(col.rawValue)")
return handler([]) // <-- here
}
documents.compactMap { queryDocumentSnapshot in
let results = try? queryDocumentSnapshot.data(as: T.self)
handler(results) // <-- here
}
}
}
init() {
collect(col: .tasks) { self.tasks = $0 }
collect(col: .projects) { self.projects = $0 }
collect(col: .notebooks) { self.notebooks = $0 }
collect(col: .notes) { self.notes = $0 }
}
}