Storing asynchronous Cloud Firestore query results in Swift
You need a dispatch group in addition to a completion
func convertToNames(arr: [String],completion:@escaping(([String]) -> ())) {
var newArr : [String] = []
let g = DispatchGroup()
for id in arr {
let docRef = db.collection("users").document(id)
g.enter()
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
let data = document.get("firstname") ?? "nil"
print("gotten data: \(data)")
newArr.append(String(describing: data))
g.leave()
} else {
print("Document does not exist")
g.leave()
}
}
}
g.notify(queue:.main) {
print("NEW ARRAY: \(newArr)")
completion(newArr)
}
}
Call
convertToNames(arr:<#arr#>) { res in
print(res)
}
Handling asynchronous Firestore data reading in tableView - iOS
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
TableView
like 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
}
addSnapShotListener triggers all the function dependent to it?
When the data changed (and at the initial load) only the code inside your listener (the ...
in your question) is called. There is no effect on functions A
or B
. So any code that needs the data from the database, has to be inside the snapshot callback, be called from there, or be otherwise synchronized with that code.
If this is surprising to you, you may be new to dealing with asynchronous callbacks. If that's the case, I recommend checking out:
- How do I save data from cloud firestore to a variable in swift?
- Storing asynchronous Cloud Firestore query results in Swift
- SwiftUI - Wait until Firestore getDocuments() is finished before moving on
- How can I change the order of functions triggered?
How to properly set State with Firestore getDocuments
The only thing that's wrong is your expectations about how getDocuments
works.
getDocuments
is asynchronous, and the callback (completion) you pass to it is executed after it returns. Print something in the callback and it'll be more clear what's actually going on.
Given that, checkDoc
cannot possibly return the correct value synchronously unless you do something to make it block (which you should not do). You will need to handle all query results asynchronously.
How can I change the order of functions triggered?
Data is loaded from Firebase asynchronously. Since that may take some time, your completion handler is called later than you might expect.
For this reason, any code that needs the data from the database, needs to be inside the completion handler, or be called from there.
So the simplest fix is to move the performSegue
into the callback:
extension HomeViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
db.collection(K.FStore.newGameCpllection)
.whereField(K.FStore.uID, isEqualTo:players[indexPath.row].uID)
.addSnapshotListener { (querySnapshot, err) in
if let err = err {
print("Error getting game db: \(err)")
} else {
for doc in querySnapshot!.documents {
print("document saved!!")
self.gameDocumentID = doc.documentID
self.db.collection(K.FStore.newGameCpllection).document(self.gameDocumentID).updateData([
K.FStore.player2Field: self.playerInfo[K.FStore.nameField]!
]) { err in
if let err = err {
print("Error updating document: \(err)")
} else {
print("Document successfully updated")
}
print("segue triggered!!!")
self.performSegue(withIdentifier: K.homeToGameScreen, sender: self)
}
}
}
}
}
}
Also see:
- Is Firebase getDocument run line by line?
- Handling asynchronous Firestore data reading in tableView - iOS
- Storing asynchronous Cloud Firestore query results in Swift, using a dispatch group to seemingly change the order of execution.
- How do I save data from cloud firestore to a variable in swift?
Related Topics
How to Load an Uiimage into a Swiftui Image Asynchronously
How to Make a Button Continually Call a Function When Held Down (Spritekit)
Optional Binding with Try? and As? Still Produces an Optional Type
Open Phone Settings Programmatically in iOS9
Performance Testing in Swift Using Tdd
Uibutton Image for Normal State in Collectionview Cell Repeats Itself Every Four Cells
Swift - Which Types to Use? Nsstring or String
Remove Grey Background on Link Clicked in iOS Safari/Chrome/Firefox
Fix CSS Hover on Iphone/Ipad/Ipod
Issue with Code Autocompletion/Syntax Highlighting in Xcode 4.X
iOS 8 - Buttons in Horizontal Scroll View Intercepting Pan Event - Scroll Does Not Work
Implementing Hmac and Sha1 Encryption in Swift
Differences Between Udid and Uuid
How to Set an Nsdate Object to Midnight
Alamofire Invalid Value Around Character 0
How to List Out All the Subviews in a Uiviewcontroller in iOS