1

why does my function's body get executed only one time although I put the body inside for loop that should be executed 7 times?

my lines of code is as follow

 override func viewDidLoad() {
            super.viewDidLoad()
            getLocationFromGeoCoding()
        }
    func getLocationFromGeoCoding() {
        for index in 0...6{
         geoCoder.reverseGeocodeLocation(sevenWorldWonderOb[index].location ?? CLLocation()) {[weak self] (placemarks, err) in
        if let err = err {
            print("Error",err.localizedDescription)
            return
        }else {
            guard let placemark = placemarks?.first else {return}
             let streetNumber = placemark.subThoroughfare
             let street = placemark.thoroughfare
             let city = placemark.locality
             let state = placemark.administrativeArea
            let country = placemark.country
            let Address = "\(streetNumber == nil ? "" : streetNumber!) \(street == nil ? "" : street!) \(city == nil ? "" : city!) \(state == nil ? "" : state!) \(country == nil ? "" : country!)"
            self?.addressLocation.append(Address)
            self?.wonderAddress.text = self?.addressLocation[index]
            }
        }

    }
}
Jawad Ali
  • 13,556
  • 3
  • 32
  • 49
Ahmed Bahgat
  • 101
  • 1
  • 9
  • Why is the loop breaking? Did you check if the `error` is printed or not? – Rob Jan 29 '20 at 11:31
  • You have `return` within your `if` and `else`. This possibly exits the loop and ends the function. – George Jan 29 '20 at 11:35
  • @Rob there is actually no error , I debugged my code and figured out that the debugger only get inside the function one time even though I encapsulated the function's body inside a loop to be executed 7 times – Ahmed Bahgat Jan 29 '20 at 11:35
  • @George_E , the debugger doesn't get inside the if statement to execute the return line – Ahmed Bahgat Jan 29 '20 at 11:36
  • @AhmedBahgat Could it not be in the `else` and `return`ing from the `guard`? – George Jan 29 '20 at 11:38
  • my function gets called only one time in viewDidLoad and this is completely true, but I put the function's body inside for loop to be executed 7 times then get out of the function going back to the viewDidLoad to execute whatever comes after it – Ahmed Bahgat Jan 29 '20 at 11:40
  • @George_E , yes you're right I put a break point on the guard line and what you have stated is what is happening with me, thank you I will try to fix that – Ahmed Bahgat Jan 29 '20 at 11:41

3 Answers3

4

You can use DispatchGroup when you are working in async. reverseGeocodeLocation works asynchronously. if you want to know when you have all addresses in your Array

func getLocationFromGeoCoding() {
let group = DispatchGroup()
        for index in 0...6{
         group.enter()
         geoCoder.reverseGeocodeLocation(sevenWorldWonderOb[index].location ?? CLLocation()) {[weak self] (placemarks, err) in
            defer {
               group.leave()
           }
        if let err = err {
            print("Error",err.localizedDescription)

        } else {
            guard let placemark = placemarks?.first else { return}
             let streetNumber = placemark.subThoroughfare
             let street = placemark.thoroughfare
             let city = placemark.locality
             let state = placemark.administrativeArea
            let country = placemark.country
            let Address = "\(streetNumber == nil ? "" : streetNumber!) \(street == nil ? "" : street!) \(city == nil ? "" : city!) \(state == nil ? "" : state!) \(country == nil ? "" : country!)"
            self?.addressLocation.append(Address)
            self?.wonderAddress.text = self?.addressLocation[index]
            }


group.notify(queue: .main, execute: { // executed after all async calls in for loop finish
    print(“Got all addresses”)
    // do whatever you want to do on getting all addresse
})

    }

playground tested

enter image description here

Jawad Ali
  • 13,556
  • 3
  • 32
  • 49
  • You should put `group.leave()` into a `defer` statement, otherwise you'll get problems if the `guard` statement fails and returns without leaving the group. – Andreas Oetjen Jan 29 '20 at 12:04
  • yes you are right ... i edited the answer according to you comment ... thank you so much – Jawad Ali Jan 29 '20 at 12:34
  • @jawadAli the answer seems to be great and I actually missed the point of handling threads and those GCD stuff, but a new problem popped up which is that my debugger doesn't get into the group.notify to execute what I have written inside, bear in mind that the debugger got inside all lines that came before the calling of group.notify , and what I have written inside the group.notify is appending what I have got from the function reverseGeocodeLocation inside an array of strings – Ahmed Bahgat Jan 29 '20 at 15:50
1

I think there is a misunderstanding is on how reverseGeocodeLocation works. Typically, I would expect that it works asynchronously.

geoCoder.reverseGeocodeLocation(sevenWorldWonderOb[index].location ?? CLLocation())
{
    [weak self] (placemarks, err) in
    print ("here in closure")
    // ...
}

This means it will print here in closure and do all the other stuff inside the closure asynchronously, when it - at some time in the future - receives the location information from some background worker thread.

But all this depends on how geoCoder implements reverseGeocodeLocation. I would expect that the closure indeed is called seven times, but you need to wait some time (please check the print statements).

Maybe you also need to update the view outlets in the main thread (using DispatchQueue.main.async), but this also depends on how the closure is being called.

Andreas Oetjen
  • 9,889
  • 1
  • 24
  • 34
0

ReverseGeocodeLocation is a type of asynchronous type and it will process its method with completion handler. So, when you are calling this method under for Loop. Your for loop works fine but because of the escaping completion handler of reverse geocoding it will not process the code after this line

geoCoder.reverseGeocodeLocation(sevenWorldWonderOb[index].location ?? CLLocation()) {[weak self] (placemarks, err) in

So, you are getting last index value of the for loop always. Because of the escaping completion handler it throws the call out of the call of the reverseGeocodingLocation and then comes back to the functionality written under this method and process that but in your case when it throws call out of the method then the loop always integrated by one and process till end index after that it will process the functionality under this method. To resolve this you need manage escaping completion handler of the reverseGeocodeLocation.

Check this stack query too.

Apps Maven
  • 1,314
  • 1
  • 4
  • 17