swift 3 DispatchGroup leave causes crash when called in helper class function

10,427

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.

Share:
10,427

Related videos on Youtube

Jim H.
Author by

Jim H.

Updated on June 13, 2022

Comments

  • Jim H.
    Jim H. almost 2 years

    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.
    Jim H. over 6 years
    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.
    Jim H. over 6 years
    with completion handlers, is it like... f1(completion: f2(completion:f3)) ?? if I need f3 to use results of f1&f2
  • Rob
    Rob over 6 years
    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 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...