3

I I have a loop with a firestore query in it that is repeated 10 times. I need to call the (completion: block) after all the 10 queries completed; Here I have my code so that it performs the (completion: block) per query but this would be too heavy on the server and the user's phone. How can I change below to accomplish what I just described?

static func getSearchedProducts(fetchingNumberToStart: Int, sortedProducts: [Int : [String : Int]], handler: @escaping (_ products: [Product], _ lastFetchedNumber: Int?) -> Void) {

        var lastFetchedNumber:Int = 0
        var searchedProducts:[Product] = []

        let db = Firestore.firestore()

        let block : FIRQuerySnapshotBlock = ({ (snap, error) in

            guard error == nil, let snapshot = snap else {
                debugPrint(error?.localizedDescription)
                return
            }

            var products = snapshot.documents.map { Product(data: $0.data()) }

            if !UserService.current.isGuest {

                db.collection(DatabaseRef.Users).document(Auth.auth().currentUser?.uid ?? "").collection(DatabaseRef.Cart).getDocuments { (cartSnapshot, error) in
                    guard error == nil, let cartSnapshot = cartSnapshot else {
                        return
                    }

                    cartSnapshot.documents.forEach { document in
                        var product = Product(data: document.data())
                        guard let index = products.firstIndex(of: product) else { return }
                        let cartCount: Int = document.exists ? document.get(DatabaseRef.cartCount) as? Int ?? 0 : 0
                        product.cartCount = cartCount
                        products[index] = product
                    }
                    handler(products, lastFetchedNumber)
                }
            }

            else {
                handler(products, lastFetchedNumber)
            }
        })

        if lastFetchedNumber == fetchingNumberToStart {

            for _ in 0 ..< 10 {

                //change the fetching number each time in the loop
                lastFetchedNumber = lastFetchedNumber + 1

                let productId = sortedProducts[lastFetchedNumber]?.keys.first ?? ""

                if productId != "" {
                    db.collection(DatabaseRef.products).whereField(DatabaseRef.id, isEqualTo: productId).getDocuments(completion: block)
                }
            }
        }

    }

as you can see at the very end I am looping 10 times for this query because of for _ in 0 ..< 10 :

if productId != "" {
                    db.collection(DatabaseRef.products).whereField(DatabaseRef.id, isEqualTo: productId).getDocuments(completion: block)
                }

So I need to make the completion: block handler to be called only once instead of 10 times here.

user12669401
  • 229
  • 4
  • 12

1 Answers1

3

Use a DispatchGroup. You can enter the dispatch group each time you call the async code and then leave each time it's done. Then when everything is finished it will call the notify block and you can call your handler. Here's a quick example of what that would look like:

let dispatchGroup = DispatchGroup()
let array = []

for i in array {
    dispatchGroup.enter()
    somethingAsync() {
        dispatchGroup.leave()
    }
}

dispatchGroup.notify(queue: .main) {
    handler()
}
Wyetro
  • 8,439
  • 9
  • 46
  • 64