Leave a rating/review
Notes: 13. Understand Core Location Components
The student materials have been reviewed and are updated as of January, 2022.
Being a framework, Core Location contains a variety of components use to work with it. You’ve already encountered a few of them. For instance, you’ve already worked with the CLLocation and the CLLocationCoordinate2D.
CLLocation
The CLLocation contains the geographical location and altitude along with the accuracy of the measurements. It also contains the speed and heading of device in motion. Of course, CL in the CLLocation stands for Core Location so a CLLocation is a Core Location Location. Say that ten times fast.
If you need just the coordinate information - that is latitude and longitude, you use the CLLocationCoordinate2D.
CLLocationManager
But how do you get a user’s location? For that, you use the CLLocationManager. This object track both large and small changes with a configurable degree of accuracy. It will report heading changes. It will monitor regions and report the range of beacons. Note, we won’t be covering beacons in this course.
So when you need a location, you’ll need to create a CLLocationManager. At which point, you create need to assign it a delegate. That delegate will receive locations. But, before you can request a location, you need to request permission. We’ll cover permissions in the next episode but only once you have permissions will you be able to access location data.
You can configure the location manager to determine the frequency and accuracy of updates. For instance, there is a desiredAccuracy property that allows you to tune how accurate you’d like the location data to be. Keep in mind, Core Location will try its best to achieve that level accuracy but it is not guaranteed. The better accuracy, the more power required - so keep that in mind when developing your app.
Activity Type
The activityType property lets Core Location know how you are using it. For instance, setting it to Fitness lets CoreLocation know the app is doing something like running or cycling, and may pause updates if there is no movement for a significant period time. There are a couple of ways to receive locations. You can either request a single location update or you can receive a constant stream of updates.
Unfortunately, life isn’t perfect. Things are going to go wrong when dealing with locations. The user may shut off his or her phone. Pesky parents may enact restrictions. Or the hardware may just take its time to get a location. Thankfully, we can respond to these issues through the delegate method locationManager didFailWithError. In this method, we’ll check our CLError enumeration against the passed in Error object.
CLError
CLError contains lots and lots of different error conditions. A common error is LocationUnknown. This means the device is unable to get the current location. You may encounter this error when Core Location starts up. This is this is the best kind of error in that you may not have to do anything although if continue to receive this error after a period of time, something else may be at play. Let’s play around with locations.
LocationManager.swift
In this demo, we’re going to create our own location manager. We’ll create an object that essentially encapsulates the CLLocationManager. Open up your sample project and press command n to create a new Swift class. Call it LocationManager and save it in the model folder. First import the core Location framework.
import CoreLocation
Now we’ll create our LocationManager class. This will an ObservableObject.
final class LocationManager: ObservableObject {
}
Now we need a property to contain the CLLocationManager. We’ll call it, locationManager.
var locationManager = CLLocationManager()
Now lets create an initializer for the class. We’ll need to set a delegate and the accuracy.
init() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
For now, we’ll use the best accuracy although you may want to change your accuracy settings based on how you use it. Of course we’re getting a compile error because we need the class to conform to the CLLocationManagerDelegate protocol.
extension LocationManager: CLLocationManagerDelegate {
}
You’ll notice that we’re getting another compile error. Our object needs to be an NSObject subclass or conform to the NSObject protocol to be a CLLocationManagerDelegate. Let’s make our object a subclass.
class LocationManager: NSObject, ObservableObject {
We also need to update our initializer.
override init() {
super.init()
Now we’re going to create a method to start location services. If the user hasn’t been prompted for permission, then iOS will prompt them. If they previously denied permission, this method won’t do anything. The user will need to manually give us permission.
func startLocationServices() {
if locationManager.authorizationStatus == .authorizedAlways || locationManager.authorizationStatus == .authorizedWhenInUse {
If we have permission, we call startUpdatingLocation. This produces an endless series of location updates. If we were only interested in one, we could call requestLocation instead.
locationManager.startUpdatingLocation()
If we don’t have permission, we need to request it. In this case, we’ll request when in use authorization.
} else {
locationManager.requestWhenInUseAuthorization()
}
}
When the user grants us permission, the delegate method locationManagerDidChangeAuthorization is called. Let’s implement that now.
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if locationManager.authorizationStatus == .authorizedAlways || locationManager.authorizationStatus == .authorizedWhenInUse {
locationManager.startUpdatingLocation()
}
}
In this case, if we have permission, we start getting locations. Now to respond to our locations. In our case, we’ll just print it out. We’ll start by creating a published property that contains the location string. Add the following:
@Published var locationString = ""
Now we’ll implement the delegate method, didUpdateLocations. We’ll get the first location and then convert it to a string.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let latest = locations.first else { return }
locationString = "location: \(latest.description)"
}
Now if we do receive an error, we can implement didFailWithError. Add the following:
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
}
Now you must cast the error as a CLError. A guard statement is a good tool for this.
guard let clError = error as? CLError else { return }
Now you can switch on the different errors. We’ll add a cast for permission denied. You’ll notice in the code completion you have lots of other errors to choose from. We’ll implement a default for now.
switch clError {
case CLError.denied:
print("Access denied")
default:
print("Catch all error")
}
Now let’s make a new view for our location manager. Press Command N to create a new SwiftUI file. Call it, “LocationView”. Start by providing an StateObject. We make it a state object so it won’t be reinitialized everytime the ui updates.
@StateObject var locationManager = LocationManager()
Now lets write the view. It’ll be a simple VStack.
VStack {
}
Add a spacer and then some text for the current location followed by another spacer.
Spacer()
Text(locationManager.locationString)
Spacer()
Finally, lets’s add a button to activate location services.
Button {
locationManager.startLocationServices()
} label: {
Text("Start Location Services")
}
And that’s it. Open up InterestingPlacesApp and set the LocationView as the startup view. Now before we can build and run, we need to let the user know why we are asking for permission. We do this in our info.plist. Open up the file and add a new key. We’re looking for a “Privacy - When In Use Usage Description”. Add the following:
We will use your location to give you the distance and directions to interesting places. Now build and run. We’ll see our first screen. Start location services by tapping a button. Now, we’ll be prompted with permission. Provide permission. Now we need to simulate our location. Under the Features menu, select location and now select Apple. This sets our location at Apple headquarters. If we want to simulate movement, set the location to freeway drive. Now you’ll see our location changing every few moments.