TrashMapper - iOS Project - CoreLocation - Part2
Table of Contents
Motivation
In the last post, I was able to tap a button on screen and begin collecting location data. I also had a stop button that would invoke stopLocationManager()
.
Within this post I’ll cover updates to the getLocation()
method, merging of start/stop updating location button, and bounding how long we search for new locations based on some tools that Apple provides us.
Location Change & Updating
Here I show that the start and stop button merged into a single button. The title of the button updates as we begin searching for current location.
I initially set the location “None” as provided Xcode Simulator. I then changed the location to “Apple” and it began updating with the headquarters coordinates. This is good and shows that the app will wait until there is a valid location before it begins updating.
Timeout for Unknown Location
Although the core location service waits for a valid location, this can be a problem. If a valid location is never found, core location will continue searching forever. For this reason a 30second timer is implemented in startLocationManager() and will call didTimeOut() method. This will then call stopLocationManager() and throw a customized NSError object to locationError variable.
func startLocationManager() {
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
updatingLocation = true
timer = Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(didTimeOut), userInfo: nil, repeats: false)
}
}
@objc func didTimeOut() {
print("***Time Out***")
if location == nil {
stopLocationManager()
locationError = NSError(domain: "MyLocationsErrorDomain", code: 1, userInfo: nil)
getStatus()
}
}
It’s helpful to see how to build a custom NSError with domain name, code, and userinfo.
Stop Updating with No Location Change
On top of incorporating a timer, I’ve also made changes to how often the location updates based on current vs new locations.
In this case, we check if locations are older than 5 seconds. If so, we ignore them. I also check the horizontal accuracy and if it’s less than 0 (invalid) it’s ignored.
If we don’t have a location, then use last good known location or if the horizontal accuracy of current location is worse than new location, swap the two.
Lastly, if the accuracy doesn’t appear changed then stop updating the location because the user hasn’t moved. This will preserve battery by disabling the radios upon stopping the location manager from updating.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let newLocation = locations.last!
print("didUpdateLocations \(newLocation)")
//Last location is old/cached, ignore and continue search
if newLocation.timestamp.timeIntervalSinceNow < -5 {
return
}
//ignore invalid accuracy
if newLocation.horizontalAccuracy < 0 {
return
}
//If there was no previous location, or the accuracy is larger than the most recent one, adopt new location
if location == nil || location!.horizontalAccuracy > newLocation.horizontalAccuracy {
//new location to be used, clear error
locationError = nil
location = newLocation
}
//current location and new location accuracy the same, stop updating location
if newLocation.horizontalAccuracy <= locationManager.desiredAccuracy {
print("locations similar")
stopLocationManager()
}
getStatus()
}
Next Steps
Now that the core location code is functional and decently robust, I’ll start adding views to capture the user post content.