'Swift: Accessing argument of closure stored as variable
I ran into some code for a location manager class and I noticed that there is a variable that holds a closure.
var locationInfoCallBack: ((_ info: LocationInformation) -> ())!
I can't seem to access the underlying arguments from the variable, which would be preferred. Is it possible to retrieve the LocationInformation
from the above variable?
My understanding is that by passing in the info parameter like this self.locationInfoCallBack(info)
the 'start' function will re-run:
In my experience it is unnecessary to recall locationManager.requestAlwaysAuthorization()
or locationManager.startUpdatingLocation()
Here's the code:
class Location: NSObject, CLLocationManagerDelegate {
static let shared = Location()
let locationManager : CLLocationManager
var locationInfoCallBack: ((_ info: LocationInformation) -> ())!
override private init() {
locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.distanceFilter = kCLLocationAccuracyNearestTenMeters
super.init()
locationManager.delegate = self
}
func start(completion: @escaping(_ info: LocationInformation) -> Void) {
self.locationInfoCallBack = completion
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func stop() {
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let mostRecentLocation = locations.last else {
return
}
let info = LocationInformation()
info.latitude = mostRecentLocation.coordinate.latitude
info.longitude = mostRecentLocation.coordinate.longitude
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(mostRecentLocation) { (placemarks, error) in
guard let placemarks = placemarks, let placemark = placemarks.first else { return }
if let city = placemark.locality,
let state = placemark.administrativeArea,
let zip = placemark.postalCode,
let locationName = placemark.name,
let thoroughfare = placemark.thoroughfare,
let country = placemark.country {
info.city = city
info.state = state
info.zip = zip
info.address = locationName + ", " + (thoroughfare as String)
info.country = country
}
self.locationInfoCallBack(info)
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
locationManager.stopUpdatingLocation()
}
}
class LocationInformation {
var city: String?
var address: String?
var latitude: CLLocationDegrees?
var longitude: CLLocationDegrees?
var zip: String?
var state: String?
var country: String?
init(city: String? = "", address: String? = "", latitude: CLLocationDegrees? = Double(0.0), longitude: CLLocationDegrees? = Double(0.0), zip: String? = "", state: String? = "", country: String? = "") {
self.city = city
self.address = address
self.latitude = latitude
self.longitude = longitude
self.zip = zip
self.state = state
self.country = country
}
}
If the underlying argument is inaccessible my current hypothesis is the variable 'locationInfoCallBack' could be used to check whether the 'start' function has completed. If this is the case why not use a simple boolean for the same result?
Solution 1:[1]
You can access the parameter when the closure is running from the code of the closure, and its value will be whatever the caller passed. It’s the same as with a function. Unless the function (or closure) is running, the parameters don’t exist.
Solution 2:[2]
I want it to be available app wide
In that case, a more suitable design would be not to use a closure. Replace the closure with:
var currentLocationInfo: LocationInformation?
The parameter to start
can be removed:
func start() {
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
And in didUpdateLocation
, replace the call self.locationInfoCallBack(info)
with a simple assignment to currentLocationInfo
:
self.currentLocationInfo = info
Now you can access from anywhere:
Location.shared.currentLocationInfo
This will give you the location that you got, the last time that didUpdateLocation
is called, and you successfully reverse-geocoded that.
Note that this could be nil
, because you might be accessing it before the first time that you successfully. reverse-geocoded a location.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | gnasher729 |
Solution 2 | Sweeper |