0

I have looked at quite a few examples of using dispatch group but I can't seem to get it to work.

I am using Google Maps for my project, and have placed markers at nearby businesses. When a marker is clicked, I want to show another view controller which has images of the place.

func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
  self.loadPhotosForSalonByPlaceId(placeID: poiItem.placeId)
  photoDispatchGroup.wait()
  let salonInfoViewController = self.addPullUpController() //show view controller


  salonInfoViewController.ImageCarouselView.images = self.salonImages
  salonInfoViewController.salonName.text = poiItem.name
  salonInfoViewController.salonAddress.text = poiItem.address
  salonInfoViewController.openAppointmentSlotArray = self.openAppointmentSlots

  self.isSalonInfoViewPresented = true
  return isSalonInfoViewPresented
}

This is what my loadPhotosForSalonByPlaceId looks like:

func loadPhotosForSalonByPlaceId(placeID: String){

    var countToLimitToFiveImages = 0
    GMSPlacesClient.shared().lookUpPhotos(forPlaceID: placeID) { (photos, error) -> Void in

        if let error = error {
            // TODO: handle the error.
            print("Error: \(error.localizedDescription)")
        } else {
            for photoMetadata in (photos?.results)! {
                if countToLimitToFiveImages == 5 {
                    break;
                }
                self.photoDispatchGroup.enter()
                self.loadImageForMetadata(photoMetadata: photoMetadata)
                self.photoDispatchGroup.leave()
                countToLimitToFiveImages += 1
            }
        }
    }
}

Am I using the enter and leave incorrectly? Or should I be notifying the main thread to continue after the lookUpPhotos completes? Because right now, the array of UIImages is empty by the time I want to show the view controller.

Thanks in advance!

The code below is what I call in my loadPhotosForSalonByPlaceId function. It converts the PhotoMetaData into a UIImage, which I append to my array. From what I understand, the look up photos and the loadplacephoto are async calls. How can I go use DispatchGroup to show my view controller after both of these tasks are finished.

func loadImageForMetadata(photoMetadata: GMSPlacePhotoMetadata) {
    var image = UIImage()
    GMSPlacesClient.shared().loadPlacePhoto(photoMetadata, callback: {
        (photo, error) -> Void in
        if let error = error {
            // TODO: handle the error.
            print("Error: \(error.localizedDescription)")

        } else {
            image = photo!
            self.salonImages.append(image)

        }
    })
}
Sishir Mohan
  • 59
  • 1
  • 4

1 Answers1

0

Your use of DispatchGroup currently is all wrong. You should call enter before an async process starts and you call leave when the async process is complete. And never call wait on the main queue. You would want to use notify. Again though, do not use a dispatch group in this case.

You have two levels of async calls here. The first is in loadImageForMetadata. That needs to be refactored to provide a completion handler. The second is in loadPhotosForSalonByPlaceId which also needs to be refactored to provide a completion handler. You also need to redo how you use the dispatch group.

Here is your refactored loadImageForMetadata:

func loadImageForMetadata(photoMetadata: GMSPlacePhotoMetadata, completion: (Bool) -> Void) {
    var image = UIImage()
    GMSPlacesClient.shared().loadPlacePhoto(photoMetadata, callback: {
        (photo, error) -> Void in
        if let error = error {
            // TODO: handle the error.
            print("Error: \(error.localizedDescription)")
            completion(false)
        } else {
            image = photo!
            self.salonImages.append(image)
            completion(true)
        }
    })
}

Here is the refactored loadPhotosForSalonByPlaceId with a completion handler and the proper use of DispatchGroup:

func loadPhotosForSalonByPlaceId(placeID: String, completion: (Bool) -> Void) {
    var countToLimitToFiveImages = 0
    GMSPlacesClient.shared().lookUpPhotos(forPlaceID: placeID) { (photos, error) -> Void in
        if let error = error {
            // TODO: handle the error.
            print("Error: \(error.localizedDescription)")
            completion(false)
        } else {
            let group = DispatchGroup()

            for photoMetadata in (photos?.results)! {
                if countToLimitToFiveImages == 5 {
                    break;
                }
                group.enter()
                self.loadImageForMetadata(photoMetadata: photoMetadata) { (success) in
                     if success {
                         countToLimitToFiveImages += 1
                     }
                     group.leave()
                }
            }

            group.notify(queue: DispatchQueue.global()) {
                completion(true)
            }
        }
    }
}

And then update your usage:

func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
    self.loadPhotosForSalonByPlaceId(placeID: poiItem.placeId) { (success) in
        if success {
            DispatchQueue.main.async {
                let salonInfoViewController = self.addPullUpController() //show view controller

                salonInfoViewController.ImageCarouselView.images = self.salonImages
                salonInfoViewController.salonName.text = poiItem.name
                salonInfoViewController.salonAddress.text = poiItem.address
                salonInfoViewController.openAppointmentSlotArray = self.openAppointmentSlots
            }
        }
    }

    self.isSalonInfoViewPresented = true
    return isSalonInfoViewPresented
}
rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Thanks for a great answer. I have a question though. What if I want to use multiple async calls? Would DispatchGroup come in use then? – Sishir Mohan Sep 06 '18 at 01:26
  • @SishirMohan Yes, a dispatch group is very useful when working with multiple async calls and you want to do something when all are finished. – rmaddy Sep 06 '18 at 01:28
  • Thanks for the quick reply. I have edited my question, could you take a look and see how I might be able to leverage Dispatch Group with the two async calls? – Sishir Mohan Sep 06 '18 at 01:33
  • I have rewritten my answer based on your updated information about `loadImageForMetadata`. – rmaddy Sep 06 '18 at 02:57