0

I want to have a class that is able to get a country name using CLGeocoder. The code below doesn't work probably because variable country is assigned to self.country before CLGeocoder finishes running. What can I do so self.country actually gets the country name from CLGeocoder?

class Place {

    let location: CLLocation
    let country: String

    init(location: CLLocation) {

        self.location = location

        var country = ""

        CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, _) in

            country = placemarks![0].country // I removed error and type checks for clarity       

        })

        self.country = country // self.country = "", but should be for example "Canada"

    }
}
lukas
  • 579
  • 2
  • 8
  • 17

1 Answers1

1

all you have to do is move self.country = country inside the completion handler. The data is returned asynchronously, which you can see quite nicely if you set breakpoints on the country = placeholder and self.country lines

You will need to remember that when you define an instance of Place in your main View Controller, the value of place.country will not initially be defined. You can either check it again after a delay to get the updated version, or you can add a delegate so that it updates the parent controller when the value is ready

here's the simple version

class Place {

    let location: CLLocation
    var country: String = "Undefined"

    init(location: CLLocation) {

        self.location = location
        CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, _) in
            self.country = placemarks![0].country! // I removed error and type checks for clarity       
        })
    }
}

and here's the more elegant version with delegates

protocol CountryUpdatedDelegate
{
    func countryUpdated(_ country : String)
}

class Place {

    let location: CLLocation
    var country: String = "Undefined"

    var delegate : CountryUpdatedDelegate!

    init(location: CLLocation) {

        self.location = location

        CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, _) in
            guard let placeMarks = placemarks as [CLPlacemark]! else {
                return
            }
            self.country = placemarks![0].country! // I removed error and type checks for clarity
            self.delegate.countryUpdated(self.country)
        })
    }
}

and then in your ViewController

class ViewController: UIViewController, CountryUpdatedDelegate {

    let place = Place(location: location!)
    place.delegate = self





func countryUpdated(_ country : String)
{
    print("Country has now been updated \(country)")
}
Russell
  • 5,436
  • 2
  • 19
  • 27
  • Didn't work... I get either "Cannot assign to property: 'country' is a 'let' constant." or "'self' captured by a closure before all members were initialized" – lukas Feb 07 '17 at 20:25
  • then change the `let` to `var` :-) – Russell Feb 07 '17 at 20:30
  • changing let to var gives the second error: "'self' captured by a closure before all members were initialized" – lukas Feb 07 '17 at 20:31
  • var country : String = "Currently undefined" – Russell Feb 07 '17 at 20:32
  • odd - I have just checked it again, and there was a missing unwrap on placemarks![0].country - updated above – Russell Feb 07 '17 at 23:19
  • Here you can see my actual code and the error: http://imgur.com/a/0ENUL – lukas Feb 07 '17 at 23:30
  • You define `let coordinate : CLLocationCoordinate2D`, but you don't assign a value. If you change it to `let coordinate : CLLocationCoordinate2D? = nil` or to 'let coordinate = CLLocationCoordinate2D()` you should be OK. The important part of the XCode error message was the second part `before all members were initialized` – Russell Feb 07 '17 at 23:37
  • I tried both .. = nil and .. = CLLocationCoordinate2D(), still the same error. http://imgur.com/a/vde0b – lukas Feb 07 '17 at 23:47
  • So I tried the second solution with delegates, and it reports the same error. – lukas Feb 08 '17 at 00:03
  • In your updated code, you have switched to using `CLLocationCoordinate2D` instead of `CLLocation` as you had originally. It definitely works as expected with `CLLocation` – Russell Feb 08 '17 at 00:12
  • Well, but isn't that weird? Why should it work with CLLocation but not with CLLocationCoordinate2D? – lukas Feb 08 '17 at 00:15