Search code examples
arraysswiftfirebasegoogle-cloud-firestoretableview

How to make table view load data from Firebase Firestore after array is appended in swift


I have this case where I get data from Firebase, put it in array, and populate it in my Table View. But the Table View loads the data before the function finishes to append data into the array. Therefore, I always get Index out of range error.

If you guys have any other solution to my case, feel free to correct my code. But for now, I only can think a way on how to make the function append the data into the array before Table View load it.

Hopefully you guys get what I mean.

Here's the code:

import UIKit
import FirebaseFirestore

var arrayCurrentProduct = [currentProduct]()
var arrayRetrievedData: [String:Any] = [:]
var arrayConvertedPrice: [String] = []

override func viewDidLoad() {
    super.viewDidLoad()
}

func initProduct(selectedProduct: String) {
    arrayCurrentProduct = referProduct(productName: selectedProduct)
}

private func referProduct(productName: String) -> [currentProduct] {
    switch productName {
    case Product.Microsoft.rawValue:
        getPrices(document: "Msft") { (_) in
            self.convertPrice(Type: "std")
            self.convertPrice(Type: "dc")
            self.tableView.reloadData()
            self.dismiss(animated: true, completion: nil)
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    default:
        break
    }
    return arrayCurrentProduct
}

private func getPrices(document: String, completion: @escaping (_ String:Any?) -> Void) {
    retrievePrice(document: document) { [weak self] (data) in
        guard let self = self else {return}
        self.arrayRetrievedData = data
        completion(self.arrayRetrievedData)
    }
}

private func convertPrice(Type: String) {
    formatterCode(formatter: formatter)
    var priceFormatted = ""
    guard let price = arrayRetrievedData[Type] else {return}
    if let formattedPrice = formatter.string(from: price as! NSNumber) {
        priceFormatted = formattedPrice
    }
    arrayConvertedPrice.append(priceFormatted)
}


func numberOfSections(in tableView: UITableView) -> Int {
    return 2
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 4
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cellShowing = tableView.dequeueReusableCell(withIdentifier: ShowingTVCell.cellIdentifier, for: indexPath) as! ShowingTVCell
    let cellInput = tableView.dequeueReusableCell(withIdentifier: InputTVCell.cellIdentifier, for: indexPath) as! InputTVCell
    
    switch indexPath.section {
    case 0:
        cellShowing.labelName.text = microsoft[indexPath.row]
        cellShowing.labelPrice.text = arrayConvertedPrice[indexPath.row] // Error index out of range
        return cellShowing
    case 1:
        cellInput.labelSpec.text = microsoft[indexPath.row]
        return cellInput
    default:
        return cellShowing
    }
}

Here's a bit of the flow:

I select a product, initProduct() called --> referProduct() called --> get price from Firebase --> convert price to currency, append to array --> loaded by Table View

I've also tried to put reloadData() in some places but it didn't work. Any help will be appreciated.

Thankyou in advance.


Solution

  • func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 4
    }
    

    You wrote a magic number (4) before the fetching data. tableView try to create a 4 row per section with no data. You should update your numbersOfRowsInSection return value after fetching data.