问题
I would like to retrieve data from my simple Firestore database
I have this database:
then I have a model class where I have a method responsible for retrieving a data which looks like this:
func getDataFromDatabase() -> [String] {
var notes: [String] = []
collectionRef = Firestore.firestore().collection("Notes")
collectionRef.addSnapshotListener { querySnapshot, error in
guard let documents = querySnapshot?.documents else {
print("Error fetching documents: \(error!)")
return
}
notes = documents.map { $0["text"]! } as! [String] // text is a field saved in document
print("inside notes: \(notes)")
}
print("outside notes: \(notes)")
return notes
}
and as a UI representation I have tableViewController. Let's take one of the methods, for example
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("tableview numberOfRowsInSection called")
return model.getDataFromDatabase().count
}
Then numberOfRows is 0 and the output in the console is:
and I am ending up with no cells in tableView. I added a breakpoint and it doesn't jump inside the listener.
And even though I have 3 of them, they are kinda "late"? They are loaded afterwards. And then the tableView doesn't show anything but console says (later) that there are 3 cells.
If needed, there is also my method for showing the cells names:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("Cells")
let cell = tableView.dequeueReusableCell(withIdentifier: "firstCell", for: indexPath)
cell.textLabel!.text = String(model.getDataFromDatabase()[indexPath.row].prefix(30))
return cell
}
but this method is not even loaded (no print in the console) and this method is written below the method with numberOfRowsInSection.
I have also 2 errors (I don't know why each line is written twice) and these are:
but I don't think it has something to do with the problem.
Thank you for your help!
回答1:
As @Galo Torres Sevilla mentioned, addSnapshotListener method is async and you need to add completion handler to your getDataFromDatabase() function.
Make following changes in your code:
Declare Global variable for notes.
var list_notes = [String]()Add completion handler to
getDataFromDatabase()method.func getDataFromDatabase(callback: @escaping([String]) -> Void) { var notes: [String] = [] collectionRef = Firestore.firestore().collection("Notes") collectionRef.addSnapshotListener { querySnapshot, error in guard let documents = querySnapshot?.documents else { print("Error fetching documents: \(error!)") return } notes = documents.map { $0["text"]! } as! [String] // text is a field saved in document print("inside notes: \(notes)") callback(notes) } }Lastly, call function on appropriate location where you want to fetch notes and assign retrieved notes to your global variable and reload
TableViewlike below:self.getDataFromDatabase { (list_notes) in self.list_notes = list_notes DispatchQueue.main.async { self.tableView.reloadData() } }
Changes in TableView:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("tableview numberOfRowsInSection called")
return self.list_notes.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("Cells")
let cell = tableView.dequeueReusableCell(withIdentifier: "firstCell", for: indexPath)
cell.textLabel!.text = String(self.list_notes[indexPath.row].prefix(30))
return cell
}
回答2:
All you need to do is refresh the table cell every time you retrieve the data. Put this code after you set your data inside the array.
self.tableView.reloadData()
来源:https://stackoverflow.com/questions/55504778/handling-asynchronous-firestore-data-reading-in-tableview-ios