This is the function I am calling, which uses the latitude and longitude, converts them into city name and country name, and then returns city name or country name depending on what I want. but in this function, when i call the function, my local variable is not updated, and nothing is printed, but after few seconds, it assigns the local variable to cityName and prints it.
func getAddressFromLatLon(lat: Double, lon: Double , cityName : Bool) ->String {
var local : String = "Nothing"
var Nation : String = ""
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: lat, longitude: lon) // <- New York
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, _) -> Void in
placemarks?.forEach { (placemark) in
local = placemark.locality ?? "Delhi"
Nation = placemark.country ?? "India"
print("Inside the geoCoder")
print(local)
}
})
if(cityName){
print("printing inside the if condition")
print(local)
return local
}
return Nation
}
var local = getAddressFromLatLon(lat: 31.0, lon: 32.0, cityName: true)
print("Outside the Function")
print(local)
Output i am getting
printing inside the if condition
Nothing
Outside the Function
Inside the geoCoder
Tanis
i want the tanis as return value for my function, but i am getting Nothing .
CodePudding user response:
Imagine that you had a helpful assistant who knows how to take a location and return a city name to you, but it takes him a while to do it.
Now you are a very busy person, so you are going to do some work up to the point that you need to identify a location. You write down the information on a piece of paper and you hand it to your assistant. He hops on a bike to find the answer while you get back to work. You work until he returns and hands you the answer.
That's what your function is doing. It runs up to the point that you call geoCoder.reverseGeocodeLocation
then it sends the location off to another processing thread that will look up the information. The current thread keeps running without waiting for an answer. It runs the if(city name)
block at the same time the other thread is doing its work.
When that other thread is done, geoCoder.reverseGeocodeLocation
calls your completionHandler
code with the result.
This is a fundamental idea of concurrency and your question suggests it caught you by surprise. You may need to do some background reading on Asynchronous coding for Apple systems.
CodePudding user response:
The geoCoder.reverseGeocodeLocation
calls its completion handler asynchronously (i.e., later). So, there are two common patterns:
Use traditional completion handler patterns. E.g.:
func reverseGeocode(lat: Double, lon: Double, completion: @escaping (Result<CLPlacemark, Error>) -> Void) { let geoCoder = CLGeocoder() let location = CLLocation(latitude: lat, longitude: lon) // <- New York geoCoder.reverseGeocodeLocation(location) { (placemarks, error) in guard error == nil, let placemark = placemarks?.first else { completion(.failure(error ?? CLError(.geocodeFoundNoResult))) return } completion(.success(placemark)) } }
And you would call it like so:
reverseGeocode(lat: 31, lon: 32) { result in switch result { case .failure(let error): print(error) case .success(let placemark): print(placemark.locality ?? "Unknown") } }
Use modern Swift concurrency, e.g.,
func reverseGeocode(lat: Double, lon: Double) async throws -> CLPlacemark { let geoCoder = CLGeocoder() let location = CLLocation(latitude: lat, longitude: lon) // <- New York return try await withCheckedThrowingContinuation { continuation in geoCoder.reverseGeocodeLocation(location) { (placemarks, error) in guard error == nil, let placemark = placemarks?.first else { continuation.resume(throwing: error ?? CLError(.geocodeFoundNoResult)) return } continuation.resume(returning: placemark) } } }
And you would call it like so:
Task { var placemark = try await reverseGeocode(lat: 31, lon: 32) print(placemark.locality ?? "Unknown") }
Now, in both of those examples, I am returning the entire CLPlacemark
. You could change these to return the String
of just locality
or county
based upon your cityName
Boolean, but the basic idea would be the same. Use completion handler or async
pattern to handle the return of the asynchronously retrieved information.