In a model's class Location
, I get the name of the current city:
var currentLatitude: Double!
var currentLongitude: Double!
var currentLocation: String!
var currentCity: String!
func getLocationName() {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: currentLatitude, longitude: currentLongitude)
geoCoder.reverseGeocodeLocation(location, completionHandler: { placemarks, error in
guard let addressDict = placemarks?[0].addressDictionary else {
return
}
if let city = addressDict["City"] as? String {
self.currentCity = city
print(city)
}
if let zip = addressDict["ZIP"] as? String {
print(zip)
}
if let country = addressDict["Country"] as? String {
print(country)
}
self.nowUpdateUI()
})
}
In view controller I want to update the UI and update my label to show the current city.
However, self.currentCity = city
happens inside of a closure. So if I just run a func in view controller:
func updateUI() {
cityLbl.text = Location.sharedInstance.currentCity
}
- I'm not getting anywhere because the closure haven't finished running.
I've been advised to add a completion handler to
getLocationName()
and inside of it, perform the call to a func that will update the UI. However, from all the tutorials out there on closures, completion handlers, it is not clear to me how to achieve that. How to construct a completion handler, pass it as an arg togetLocationName()
and how to callgetLocationName
from view controller?
To handle this situation you have multiple option.
Create
delegate/protocol
with your Location classViewController
and declare its instance in yourLocation
class. After then in thecompletionHandler
ofreverseGeocodeLocation
call this delegate method. Check Apple documentation onProtocol
for more details.You can create
completionHandler
with yourgetLocationName
method ofLocation
class.Add
completionHandler
withgetLocationName
and called thatcompletionHandler
inside thecompletionHandler
ofreverseGeocodeLocation
like this way.Now in
ViewController
where you are calling this function call yourupdateUI
method inside the completion block.You can add observer for
(NS)NotificationCenter
.(NS)NotificationCenter
and then post the notification inside thecompletionHandler
ofreverseGeocodeLocation
. You can get more detail on this with this StackOverflow Post.This entire piece of your code:
is already happening in the completionHandler (which happens after everything is finished) Just also run your
updateUI()
inside the completionHandler. So your end code would be :The reason you have to use
DispatchQueue.main
is because your completionHandler is on a backgroundqueue but you MUST always do you UI related stuff from your mainQueue—so users get the fastest changing in their UI without any glitches. Imagine if you were doing on a background thread and it was happening slow