简体   繁体   中英

TableView loaded before fetching data Firebase / Swift

i have a FireBase database, inside i have a table of products and another table of orders with ids of these products, what i am trying to do is to get products from table of products based on ids inside table of orders, since FireBase will only allow me to get the products one by one, my tableview is loaded before i get all products that are referenced inside the orders table.

heres how i did that:

struct Product: Decodable, Encodable{
    var id: String?
    var ref: String
    var description: String
    var type: String
    var price: String
    var qtyOrdred:Int?
}

struct Order:Decodable, Encodable {

    var id: String?
    var isValide: Bool
    var madeBy: String
    var info: String?
    var ordredProd: [OrderedProduct]

}

struct OrderedProduct:Decodable, Encodable {
    var id: String
    var qty: Int
}

 func getData(completion: @escaping ([Product])->Void){
        var allProduct = [Product]()
        for product in orderedProduct {
            getProductWithKey(qty: product.qty, key: product.id) { (p) in
                print(p.ref)
                allProduct.append(p)
            }
}
}

func getProductWithKey(qty: Int,key: String, completion: @escaping (Product)->Void) {
    Database.database().reference().child("products").child(key).observeSingleEvent(of: .value) { (snap) in
        if let productObject = snap.value as? [String: Any]
        {
            if let ref  = productObject["ref"],
                let price  = productObject["price"],
                let type = productObject["type"],
                let description = productObject["description"],
                let id = productObject["id"]{
                let p = Product(id: id as? String, ref: ref as! String, description: description as! String, type: type as! String, price: price as! String, qtyOrdred: qty)
                completion(p)
            }
        }
    }
}

i call it like this:

override func viewWillAppear(_ animated: Bool) {
        self.getData { (ps) in
            print(ps)
            self.tableView.reloadData()
        }
    }

The problem is that it always print an empty array, and my tableview data never changes

You don't return from getData completion, you need a dispatch group

let g = DispatchGroup()
func getData(completion: @escaping ([Product])->Void){
     var allProduct = [Product]()
     for product in orderedProduct { 
        g.enter()
        getProductWithKey(qty: product.qty, key: product.id) { (p) in
            print(p.ref)
            allProduct.append(p)
            g.leave()
        }
     }  
     g.notify(queue:.main) {
        completion(allProduct)
     }
}

Your getData function is returning as soon as the for loop is finished. As the call inside the loop is async there isn't any data when the loop finishes.

Instead of reloading the table just insert rows as they arrive.

for product in orderedProduct {
   getProductWithKey(qty: product.qty, key: product.id) { [weak self] (p) in

      guard let self = self else { return }
      allProduct.append(p)
      guard let index = allProduct.firstIndex(of: p) else { return }
      self.tableView.insertRow(at: IndexPath(row: index, section: 0))
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM