0

I am new to swift language and I have problem that I can't solve.

After running my app i get output of empty String: nil

So, my question is how to add value to variable inside closure?

Because when I add line inside closure print(self.latLong) I get output of coordinates, but I need that value inside of variable because I need to manipulate with that variable later in my code and I don't want to write all functionality inside that closure

This is my code:

import UIKit
import CoreLocation
import Firebase

var latLong: String!

override func viewDidLoad() {
    super.viewDidLoad()

    findCordiante(adress: "Cupertino, California, U.S.")
    print(latLong)
}

func findCordiante(adress:String){

    let geocoder = CLGeocoder()
    geocoder.geocodeAddressString(adress) {
        placemarks, error in

        if (placemarks != nil){
            let placemark = placemarks?.first
            let lat = placemark?.location?.coordinate.latitude
            let lon = placemark?.location?.coordinate.longitude

            self.latLong = String(describing: lat!) + "," + String(describing: lon!)

        }else{
            //handle no adress
             self.latLong = ""
        }
    }

}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Sven
  • 63
  • 1
  • 11
  • 1
    Possible duplicate of [Swift closure async order of execution](https://stackoverflow.com/questions/39880506/swift-closure-async-order-of-execution) – jscs Dec 24 '17 at 15:15

2 Answers2

0

The latLong is nil because print(latLong) is called immediately after the view is loaded, before the value of latLong is set. You can define a function to handle the latLong and call it in the closure.

import UIKit
import CoreLocation
import Firebase

var latLong: String!

override func viewDidLoad() {
    super.viewDidLoad()

    findCordiante(adress: "Cupertino, California, U.S.")
    print(latLong)
}

func findCordiante(adress:String){

    let geocoder = CLGeocoder()
    geocoder.geocodeAddressString(adress) {
        placemarks, error in

        if (placemarks != nil){
            let placemark = placemarks?.first
            let lat = placemark?.location?.coordinate.latitude
            let lon = placemark?.location?.coordinate.longitude

            self.latLong = String(describing: lat!) + "," + String(describing: lon!)
            //Do something with latLong now 
            self.doSomething(withlatLong: self.latLong)

        }else{
            //handle no adress
            self.latLong = ""
        }
    }

}

private func doSomething(withlatLong latLong:String) {
    //Do whatever you want with latLong 
    print(latLong)
}
ilovecomputer
  • 4,238
  • 1
  • 20
  • 33
  • Thank you for answer, but i need to save coordinates in variable. How can i do it? – Sven Dec 24 '17 at 17:00
  • What do you mean `save coordinates in variable`? `self.latLong` will be set after `geocodeAddressString` finish in your code. If you want to be notified when `self.layLong` is set, you can use `didSet` or `willSet` – ilovecomputer Dec 24 '17 at 17:12
0

The problem is that geocodeAddressString runs asynchronously (i.e. it takes so long they have written it to return immediately and call its closure later, when the request is done), so viewDidLoad is printing latLong well before this property was eventually set in the geocodeAddressString completion handler closure.

The solution is to adopt asynchronous programming pattern in your own code. For example, employ your own completion handler pattern:

override func viewDidLoad() {
    super.viewDidLoad()

    findCordiante(adress: "Cupertino, California, U.S.") { string in
        // use `string` here ...

        if let string = string {
            self.latLong = string
            print(string)
        } else {
            print("not found")
        }
    }

    // ... but not here, because the above runs asynchronously and it has not yet been set 
}

func findCordiante(adress:String, completionHandler: @escaping (String?) -> Void) {
    let geocoder = CLGeocoder()
    geocoder.geocodeAddressString(adress) { placemarks, error in
        if let location = placemarks?.first?.location, location.horizontalAccuracy >= 0 {
            completionHandler("\(location.coordinate.latitude), \(location.coordinate.longitude)")
        } else {
            completionHandler(nil)
        }
    }
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044