问题
I'm trying to get the total of a shopping cart in Firestore by executing a series of for loop calls that calculate the sum of items in the shopping cart.
The shopping cart is stored in a table called carts and the view upon appearing is pulled into an in-application dictionary called cartCache, of the type [String: Int]? where the key is the UID for the item and the value is the quantity of the item. However, the price of the item is stored in another collection called items. I only want to calculate the sum when the view appears or disappears, and then make it appear in a UILabel.
Thus, in order to obtain the sum and report it to a user (by modifying a label, or writing it to the cart), I need to iterate over each item in my cart and grab the price via an API call getDocument(completion:). However, this means that the calculation of the sum is asynchronous, which is not necessarily a problem, but I'm not sure of how to give a completion handler to a for loop. Is there an easier way to do this?
for item in self.cartCache!.keys
{
let doc = self.itemCollectionRef.document(item).getDocument
{
(itemDocument, error) in
{
guard let itemDocument = itemDocument, itemDocument.exists else {return}
sum += itemDocument.data()!["price"] as! Int
}
}
}
//Do something with sum (no guarantee at this point that sum is actually calculated, how do I add
//a completion handler to a for loop or obtain multiple field's values at once?
回答1:
What you need to use is a DispatchGroup
. DispatchGroup
s allow you to run multiple asynchronous functions at once, and have one "completion handler" that is called once every asynchronous function completes. Some basic code looks something like this:
//Create your dispatch group. This is going to manage all of your asynchronous tasks and run some code when they complete
let group = DispatchGroup()
for x in array {
//Call enter before the asynchronous function
group.enter()
asnynchronousFunction(completion: {
//Call leave once the asynchronous function completes
group.leave()
})
}
group.notify(queue: .main, execute: {
//Inside of group.notify, run whatever code you want to run upon completion of all asynchronous functions.
//group.notify is your "completion handler"
})
So, for your instance, it would look something like this:
let group = DispatchGroup()
for item in self.cartCache!.keys
{
group.enter()
let doc = self.itemCollectionRef.document(item).getDocument
{
(itemDocument, error) in
{
guard let itemDocument = itemDocument, itemDocument.exists else {return}
sum += itemDocument.data()!["price"] as! Int
group.leave()
}
}
}
group.notify(queue: .main, execute: {
//All of your asynchronous functions have completed. You are ready to move on
})
Source: https://developer.apple.com/documentation/dispatch/dispatchgroup
More in depth: https://www.raywenderlich.com/5371-grand-central-dispatch-tutorial-for-swift-4-part-2-2#toc-anchor-002
来源:https://stackoverflow.com/questions/58368281/need-to-obtain-shopping-cart-sum-after-series-of-getdocument-price-lookups