Home > Blockchain >  How to query Firebase Firestore with a method that works with any model
How to query Firebase Firestore with a method that works with any model

Time:09-12

I have several different models, and would like to create a method in my view model to pass in any struct and query Firebase using the parameters passed into the method. My code is incorrect, but I am trying to figure out how to: 1) write logic to tell the function which @Published variable I would like it to access, 2) allow the method to use any of my model structs which all conform to codable by passing the struct name as an argument into the method and return a type of that struct. Here's the code I have written.

import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift
import SwiftUI
import simd

class ShowDataViewModel: ObservableObject {
    
    @Published var modelOneData = [modelOne]()
    @Published var modelTwoData = [modelTwo]()
    
    private var db = Firestore.firestore()
    
    func getFromFirebase<T: Codable, X: Published>(collectionName: String, structToGetData: T, inputPublishedVar: X) {
        db.collection(collectionName).addSnapshotListener { (querySnapshot, error) in
            guard let documents = querySnapshot?.documents else {
                print("Nothing")
                return
            }
            
            self.inputPublishedVar = documents.compactMap { QueryDocumentSnapshot -> T? in
                return try? QueryDocumentSnapshot.data(as: structToGetData.self)
            }
        }
    }
}

CodePudding user response:

Your function doesn’t quite make sense to me, or perhaps you need to provide more context.

Instead of passing the published variable as a paramter, you need to pass a callback that returns the data fetched from Firebase.

For example:

class ShowDataViewModel: ObservableObject {

@Published var modelOneData = [ModelOne]()
@Published var modelTwoData = [ModelTwo]()
@Published var error: Error?

private var db = Firestore.firestore()

func getFromFirebase<T: Codable>(collectionName: String, callback: ([T]?) -> Void) {
    db.collection(collectionName).addSnapshotListener { [weak self](querySnapshot, error) in
        do {
            if let error = error {
                throw error
            }
            guard let documents = querySnapshot?.documents else {
                callback(nil)
                return
            }
            let data = try documents.compactMap { QueryDocumentSnapshot -> T? in
                return try QueryDocumentSnapshot.data(as: T.self)
            }
            callback(data)
        } catch {
            self?.error = error
        }
    }
}

func getModelOneData() {
    getFromFirebase(collectionName: "ModelOneCollection") { [weak self] (data: [ModelOne]?) in
        self?.modelOneData = data ?? []
    }
}

func getModelTwoData() {
    getFromFirebase(collectionName: "ModelTwoCollection") { [weak self] (data: [ModelTwo]?) in
        self?.modelTwoData = data ?? []
    }
}

}

Then from your view you just call the functions getModelOneData() or getModelTwoData()

I’m not sure if the code will compile though, as I typed it from my iPhone.

Hope it makes sense :)

  • Related