10

I'm using DispatchGroup.enter() and leave() to process a helper class's reverseG async function. Problem is clear, I'm using mainViewController's object to call mainViewControllers's dispatchGroup.leave() in helper class! Is there a way to do it?

Same code works when reverseG is declared in the main view controller.

class Geo {
    var obj = ViewController()

    static func reverseG(_ coordinates: CLLocation, _ completion: @escaping (CLPlacemark) -> ()) {
        let geoCoder = CLGeocoder()
        geoCoder.reverseGeocodeLocation(coordinates) { (placemarks, error) in
            if let error = error {
                print("error: \(error.localizedDescription)")
            }
            if let placemarks = placemarks, placemarks.count > 0 {
                let placemark = placemarks.first!
                completion(placemark) // set ViewController's properties
            } else {
                print("no data")
            }
            obj.dispatchGroup.leave() // ** ERROR **
        }
    }


}

Function call from main view controller

dispatchGroup.enter()
Geo.reverseG(coordinates, setValues) // completionHandler: setValues

dispatchGroup.notify(queue: DispatchQueue.main) {

    // call another function on completion

}
Jim H.
  • 285
  • 1
  • 2
  • 15
  • Every `leave` call must have an associated `enter` call. If you call `leave` without having first called `enter`, it will crash. The issue here is that you're calling `enter` on some group, but `reverseG` is calling `leave` on some other instance of `ViewController`. I'd suggest passing the `DispatchGroup` as a parameter to your `reverseG` method. Or, better, `reverseG` shouldn't leave the group, but rather put the `leave` call inside the completion handler that `reserveG` calls. – Rob Aug 06 '17 at 17:44
  • Exactly, already have done that and it worked. Thanks – Jim H. Aug 06 '17 at 17:50

3 Answers3

11

Every leave call must have an associated enter call. If you call leave without having first called enter, it will crash. The issue here is that you're calling enter on some group, but reverseG is calling leave on some other instance of ViewController. I'd suggest passing the DispatchGroup as a parameter to your reverseG method. Or, better, reverseG shouldn't leave the group, but rather put the leave call inside the completion handler that reserveG calls.

dispatchGroup.enter()
Geo.reverseG(coordinates) { placemark in
    defer { dispatchGroup.leave() }

    guard let placemark = placemark else { return }

    // use placemark here, e.g. call `setValues` or whatever
}

dispatchGroup.notify(queue: DispatchQueue.main) {
    // call another function on completion
}

And

class Geo {
    // var obj = ViewController()

    static func reverseG(_ coordinates: CLLocation, completion: @escaping (CLPlacemark?) -> Void) {
        let geoCoder = CLGeocoder()
        geoCoder.reverseGeocodeLocation(coordinates) { placemarks, error in
            if let error = error {
                print("error: \(error.localizedDescription)")
            }
            completion(placemarks?.first)

            // obj.dispatchGroup.leave() // ** ERROR **
        }
    }

}

This keeps the DispatchGroup logic at one level of the app, keeping your classes less tightly coupled (e.g. the Geo coder doesn't need to know whether the view controller uses dispatch groups or not).

Frankly, I'm not clear why you're using dispatch group at all if there's only one call. Usually you'd put whatever you call inside the completion handler, simplifying the code further. You generally only use groups if you're doing a whole series of calls. (Perhaps you've just simplified your code snippet whereas you're really doing multiple calls. In that case, a dispatch group might make sense. But then again, you shouldn't be doing concurrent geocode requests, suggesting a completely different pattern, altogether.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • I just wanted reverse geo functions in separate class and use placemarks in main class to set properties(not call any callback function). function1's result is needed for function2 and function3 needs results from f1&f2 and I'm using completion handlers to only set main class properties. f1 will set property to be used in f2 and so on – Jim H. Aug 06 '17 at 18:09
  • with completion handlers, is it like... f1(completion: f2(completion:f3)) ?? if I need f3 to use results of f1&f2 – Jim H. Aug 06 '17 at 18:16
  • It's hard to say without more context. Given that we've answered the dispatch group question here, I might suggest you post a new question with a broader context showing us what you're doing. If you've got code that's working and you want feedback on the design, perhaps http://codereview.stackexchange.com is a better forum. Or if it's not working, go ahead and post a separate question on Stack Exchange. Either way, feel free to add comment with the link to that other question. But we've already strayed too far from your original dispatch group question... – Rob Aug 06 '17 at 18:21
0

Passed dispatchGroup as parameter with function call and it worked.

Geo.reverseG(coordinates, dispatchGroup, setValues)
Jim H.
  • 285
  • 1
  • 2
  • 15
0

my two cents to show how can work: (maybe useful for others..)

//  Created by ing.conti on 02/02/21.
//

import Foundation

print("Hello, World!")
let r = AsyncRunner()
r.runMultiple(args: ["Sam", "Sarah", "Tom"])





class AsyncRunner{
    static let shared = AsyncRunner()
    
    let dispatchQueue = DispatchQueue(label: "MyQueue", qos:.userInitiated)
    let dispatchGroup = DispatchGroup.init()
    
    
    func runMultiple(args: [String]){
        
        let count = args.count
        
        for i in 0..<count {
            
            dispatchQueue.async(group: dispatchGroup) { [unowned self] in
                dispatchGroup.enter()
                self.fakeTask(arg: args[i])
            }
        }
        
        _ = dispatchGroup.wait(timeout: DispatchTime.distantFuture)
    }
    
    
    
    func fakeTask(arg: String){
        
        for i in 0..<3 {
            print(arg, i)
            sleep(1)
        }
        dispatchGroup.leave()
    }    
}
ingconti
  • 10,876
  • 3
  • 61
  • 48