so I have a problem with firebase since it's async and I don't know how to work with that properly. My application has feed of products for sale, which I'm getting from firebase database and firebase storage(pictures). The problem is that I don't know when or how to reload my UITableView to get all the data without getting nil error. And it's even more complicated, because first I need to get my salePost information from firebase and only after that I can obtain user Information which is also in tableview cell.
This is how I getting my salePosts
func getSalePosts() -> [SalePost]{
var salePosts = [SalePost]()
FIR_DATABASE_SALE_POSTS.queryLimitedToFirst(NUMBER_OF_LOADED_POSTS).observeEventType(.Value, withBlock: { (snapshot) in
for snap in snapshot.children{
let imgString = snap.value["image"] as! String!
let imgNSURL = NSURL(string: imgString)
let imgNSDATA = NSData(contentsOfURL: imgNSURL!)
let downloadedImg = UIImage(data: imgNSDATA!)
let newSalePost = SalePost(title: snap.value["title"] as! String!,
userUID: snap.value["authorUID"] as! String!,
postBody: snap.value["body"] as! String!,
amountAvailable: snap.value["amountAvailable"] as! String!,
unit: snap.value["unit"] as! String!,
price: snap.value["price"] as! String!,
image: downloadedImg!)
salePosts.append(newSalePost)
print("num of posts \(salePosts.count)")
}
}) { (error) in
print(error.localizedDescription)
}
return salePosts
}
And this is func which is getting UserInfo (I will get the "userUID" from salePost object)
func getProducer(userUID:String) -> Producer{
var newProducer:Producer?
FIR_DATABASE_USERS.child(userUID).observeEventType(.Value, withBlock: { (snap) in
print(snap.description)
let imgString = snap.value!["profilePic"] as! String!
let imgNSURL = NSURL(string: imgString)
let imgNSDATA = NSData(contentsOfURL: imgNSURL!)
let downloadedImg = UIImage(data: imgNSDATA!)
let someProducer = Producer(
displayName: snap.value!["displayName"] as! String!,
address: snap.value!["address"] as! String!,
mobile: snap.value!["mobile"] as! String!,
email: snap.value!["email"] as! String!,
password: "",
bio: snap.value!["bio"] as! String!,
profilePic: downloadedImg!,
openingHour: snap.value!["openingHour"] as! String!,
closingHour: snap.value!["closingHour"] as! String!)
newProducer = someProducer
print(newProducer?.description)
}) { (error) in
print(error.localizedDescription)
}
return newProducer!
}
So as I said, I don't know when, where and how to call these functions (in my feed controller) to make it "synchronous" and to get all that data to the tableview Cells right. I will appreciate every help. I tried google it and even find it here, but nothing helped me.
Once you created all your
salePosts
you should calltableView.reloadData()
. So right after you loop to create where you append yournewSalePost
to yoursalePosts
. Since this is an asynchronous call returning your salePosts at the end of your function is useless.I'm assuming that you have an attribute
tableView
of your viewController and you set thedelegate
anddatasource
of yourtableView
to be yourviewController
.Do not forget to call
tableView.reloadData()
on the main thread.eg:
Dispatch.main.async { self.tableView.reloadData() }
.For future users seeing this post, you can also use Dispatch group to handle the async property of data retrieval from Firebase. For more on the working of dispatch groups you can read Dispatch Groups in Swift 3. The syntax is more or less same in Swift 4 as well. This is how i have implemented dispatch for my project with a tableview.
I have used snapshot.children, because of how i have structured my data. How you are getting the snapshot will depend on the structure you have. The dispatch group has to be used within the
dbRef.observe
closure.