0

I have a function that should have a completion-handler which should only be called if everything inside of it is actually completed. That is my function:

static func getWishes(dataSourceArray: [Wishlist], completion: @escaping (_ success: Bool, _ dataArray: [Wishlist]) -> Void){
    
    var dataSourceArrayWithWishes = dataSourceArray
    
    let db = Firestore.firestore()
    let userID = Auth.auth().currentUser!.uid
    
    for list in dataSourceArray {
        db.collection("users").document(userID).collection("wishlists").document(list.name).collection("wünsche").order(by: "wishCounter").getDocuments() { ( querySnapshot, error) in
            if let error = error {
                print(error.localizedDescription)
                completion(false, dataSourceArrayWithWishes)
            } else {
                // append every Wish to array at wishIDX
                for document in querySnapshot!.documents {
                    let documentData = document.data()
                    let imageUrlString = document["imageUrl"] as? String ?? ""
                    
                    let imageView = UIImageView()
                    imageView.image = UIImage()
                    if let imageUrl = URL(string: imageUrlString) {
                        let resource = ImageResource(downloadURL: imageUrl)
                        imageView.kf.setImage(with: resource) { (result) in
                            switch result {
                            case .success(_):
                                dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(name: name, link: link, price: price, note: note, image: imageView.image!, checkedStatus: false))
                                completion(true, dataSourceArrayWithWishes)
                                print("success")
                            case .failure(_):
                                print("fail")
                            }
                        }
                    } else {
                       dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(name: name, link: link, price: price, note: note, image: imageView.image!, checkedStatus: false))
                    } 
                }
            }
        }
    }
}

The problem lies in imageView.kf.setImage... Right now I am calling completion after the first .success but the function should only be completed if the for-loop and all the setImages are being finished. I've tried a couple of things now but I can not make it work. So I wonder what's best practice for this case?

Chris
  • 1,828
  • 6
  • 40
  • 108
  • 1
    Related: https://stackoverflow.com/questions/45484563/completion-gets-called-soon/45485143#45485143 – vadian Jul 16 '20 at 16:24
  • @vadian exactly what I need! I didnt find anything when I searched for it, Thanks man! – Chris Jul 16 '20 at 16:30

1 Answers1

1

Here is how you use DispatchGroup for this... You need DispatchGroup to get notified when the asynchronous loop is finished

 static func getWishes(dataSourceArray: [Wishlist], completion: @escaping (_ success: Bool, _ dataArray: [Wishlist]) -> Void){
       
       var dataSourceArrayWithWishes = dataSourceArray
       
       let db = Firestore.firestore()
       let userID = Auth.auth().currentUser!.uid
        let group = DispatchGroup()
    
       for list in dataSourceArray {
        
        group.enter()
           db.collection("users").document(userID).collection("wishlists").document(list.name).collection("wünsche").order(by: "wishCounter").getDocuments() { ( querySnapshot, error) in
            defer{ group.leave() }
               if let error = error {
                   print(error.localizedDescription)
                   completion(false, dataSourceArrayWithWishes)
               } else {
                   // append every Wish to array at wishIDX
                   for document in querySnapshot!.documents {
                    group.enter()
                       let documentData = document.data()
                       let imageUrlString = document["imageUrl"] as? String ?? ""
                       
                       let imageView = UIImageView()
                       imageView.image = UIImage()
                       if let imageUrl = URL(string: imageUrlString) {
                           let resource = ImageResource(downloadURL: imageUrl)
                           imageView.kf.setImage(with: resource) { (result) in
                            defer{ group.leave() }
                               switch result {
                               case .success(_):
                                   dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(name: name, link: link, price: price, note: note, image: imageView.image!, checkedStatus: false))
                                   
                               case .failure(_):
                                   print("fail")
                               }
                           }
                       } else {
                          dataSourceArrayWithWishes[wishIDX].wishes.append(Wish(name: name, link: link, price: price, note: note, image: imageView.image!, checkedStatus: false))
                       }
                   }
               }
           }
       }
    
    group.notify(queue: DispatchQueue.main) {
        completion(true, dataSourceArrayWithWishes)
        print("success")
    }
   }
Jawad Ali
  • 13,556
  • 3
  • 32
  • 49