Reputation: 15
I have a working connection to Firebase realtime database. The observeSingleEvent is triggered by IBAction button tapped. First call to fetch the data fails. Second push of the button fetches the data OK. What would be a way to get the data with the first call? I've tried calling observeSingleEvent several times, tapping the button programmatically and adding a wait to allow observeSingleEvent to complete. Even added the completion block when getting desperate. Any hint as what I am doing wrong would be highly appreciated.
--- EDIT ---
Fixed the code below as @rob suggested. Only addition to Rob's answer was that @escaping was needed in definition of completion block. Now works beautifully.
var prodNo: String = "XXX000000"
var productDataLocal: [String: String] = ["ProdNo": "", "Location": ""]
@IBAction func findTapped(_ sender: Any) {
fetchFirebaseData { () -> () in
if productDataLocal["ProdNo"] == prodNo { // check that the data matches the product
// launch a segue (that is detached from button)
self.performSegue(withIdentifier: "moveToDetail", sender: nil)
}
else {
showError("Product \(prodNo) can't be found.")
}
}
} // findTapped
func fetchFirebaseData(completion: @escaping () -> ()) { // --- EDIT ---@escaping addedd
// check if this product can be found in Firebase!
self.ref.child("Id").child("\(prodNo)").observeSingleEvent(of: .value, with:
{ (snapshot) in
// try if this product can be found in Firebase
let tempData = snapshot.value as? Dictionary<String, Any>
if let actualData = tempData {
self.copyData(actualData)
}
completion() // --- EDIT --- completion moved here
}) { (error) in
print(error.localizedDescription)
completion() // --- EDIT ---and completion moved here
}
// --- EDIT -- completion() removed from here
} // fetchFirebaseData
func copyData(_ actualData: Dictionary<String, Any>) {
dump(actualData) // for debugging what "actualData" actually contains
// this is always OK when execution gets here
self.productDataLocal["ProdNo"] = actualData["ProdNo"] as? String
self.productDataLocal["Location"] = actualData["Location"] as? String
}
The dump of "actual" data works and produces correct data - when it gets executed. The problem is that I can't find any way to get this code to execute but pressing the button second time. (And yes the "ProdNo" is repeated in the JSON structure so that I can check that it matches the requested product number.) --- EDIT --- the code above now works and problem solved
▿ 2 key/value pairs
▿ (2 elements)
- key: "ProdNo"
- value: XXX000000 #0
- super: NSMutableString
- super: NSString
- super: NSObject
▿ (2 elements)
- key: "Location"
- value: "Warehouse 1" #1
- super: NSMutableString
- super: NSString
- super: NSObject
Upvotes: 1
Views: 179
Reputation: 5073
You are calling you completion block synchronously. You should not be calling it until observeSingleEvent completes. If you move your completion block to your observeSingleEvent handler you should get the results you are looking for:
func fetchFirebaseData(completion: @escaping () -> ()) { // --- EDIT --- added "@escaping
// check if this product can be found in Firebase!
self.ref.child("Id").child("\(prodNo)").observeSingleEvent(of: .value, with: { (snapshot) in
// try if this product can be found in Firebase
let tempData = snapshot.value as? Dictionary<String, Any>
if let actualData = tempData {
self.copyData(actualData)
}
completion()
}) { (error) in
print(error.localizedDescription)
completion()
}
} // fetchFirebaseData
Upvotes: 1