Tutorial: Connecting Core Location to a SwiftUI App
How to connect Apple’s Core Location service to a SwiftUI App
Project Setup
First, let’s create a new SwiftUI Xcode project. I’m using the iOS App template:
I’ve selected SwiftUI as the interface and Swift as the language:
You should now have a basic setup that looks like this:
Location Data Manager
In order to access Core Location data, we are going to set up a Location Data Manager class that will contain a CLLocationManager variable.
What is CLLocationManager?
CLLocationManager (Apple Documentation) is an object that is designed to manage Core Location based behavior. When setting this up in our project, we will do the below:
- Set up Class containing CLLocationManager
- Set up delegate objects confirming to CLLocationManagerDelegate protocol (Apple Documentation) that will receive updates from CLLocationManager
- Set up a @Published variable so that our SwiftUI view will update when the status changes in our Class
Setting Up CLLocationManager In Our App
Add a new Swift file to your project:
Your new Swift file should look like this:
Import CoreLocation into this file, and add the below code:
This sets up our basic CLLocationManager object, and sets the LocationDataManager class as its delegate, so when there are updates to CLLocationManager, they will be passed along by the LocationDataManager class to our app.
If interested, I encourage you to type “locationManager” in this class and look through all the different populated items that this object can be customized to react to:
Requesting Authorization of Location Data From User
As you can probably imagine, a user’s location data is very sensitive, so we will need to request authorization from the user before we are allowed to access their location data. For the purposes of this tutorial, we will only be requesting location data “When in Use”, which means that location updates will only occur when a user is actively using our app. More on this topic can be found on this Apple Documentation site.
First, we will add a Info.plist description for the NSLocationWhenInUseUsageDescription
key. This text is what will be displayed on the privacy pop up the user receives on their device when we first try to activate the Core Location.
Navigate to the Info tab on the top level of the Xcode navigation:
Add a new item with the “Privacy — Location When In Use Usage Description” key:
Add a string on the right side — this is what will appear on the iOS alert pop up:
Now we are ready to make the authorization request. We will be adding a function to our LocationDataManager delegate that will handle the different outcomes of authorization requests (authorized, not authorized, or not yet determined.
Add this function to your code:
Requesting the Location Data from Core Data
Now that we have the authorization request in place, we are ready to actually request the data from Core Data. The location data you need will depend on your use case. If you need the most precise and regular location data, you would want to call the startUpdatingLocation() function, however, note that this is the most power-consuming option. Other options, such as startMonitoringSignificantLocationChanges() and startMonitoringVisits() are lower power, but also not as precise or as frequently updated. For our tutorial, I’ll be adding the requestLocation() call, which gets a single, one time location data point. More information on these different options can be found on the Apple Documentation site.
Add the below call to the .authorizedWhenInUse result in the .authorizationStatus switch to call it when our authorization is approved.
manager.requestLocation()
We will also need to tell our LocationDataManager class to conform to ObservableObject in order to link it up with our SwiftUI view, so that updates to the Core Location data propagate updates across our app.
Your LocationDataManager should now look like this:
In our ContentView, we will want the SwiftUI view to update when .authorizationStatus changes. This way, our app will update when the authorization is received. In order to update our SwiftUI view, I’m going to add a new @Published variable and add updates in the .authorizationStatus switch statement.
First, add this to the LocationDataManager class:
@Published var authorizationStatus: CLAuthorizationStatus?
Then, for each of the switch statement cases, set the new “authorizationStatus” variable like this:
case .authorizedWhenInUse: // Location services are available.authorizationStatus = .authorizedWhenInUselocationManager.requestLocation()break
When you add this throughout the switch statement, it should look like this:
We need to add in 2 more delegate functions to our Class that inherit from the CLLocationManagerDelegate protocol before we can move on to the UI. We will not be fully exploring these in the scope of this tutorial, but I do want to briefly touch on them as they are required to not have errors at build time:
locationManager(_:didUpdateLocations:) — Apple Documentation
This is an instance method that will tell our delegate that new location data is available. Add this to the LocationDataManagerClass:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {// Insert code to handle location updates}
locationManager(_:didFailWithError:) — Apple Documentation
This is an instance method that would update our delegate if the location manager was unable to retrieve location data. For the purposes of our tutorial, we will be printing this error if one arises:
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {print("error: \(error.localizedDescription)")}
The final LocationDataManager Class should look like this:
Connecting the Location Data to a SwiftUI View
Now that we have set up our call to get the Core Location data, we need to connect it to our SwiftUI view.
First, we need to add in a new @StateObject instance of our LocationDataManager class, so that we can access the LocationDataManger data.
@StateObject var locationDataManager = LocationDataManager()
You can learn more about @StateObject and find the right solution for your App with this helpful Hacking With Swift article.
To help us visualize what’s happening, I’m going to reuse the switch statement from our LocationDataManger, but use SwiftUI text views. To access the location data, we can call our locationManager variable within the LocationDataManager class, and access its .location property, which is the most recently retrieved user location, in the form of a CLLocation item. From here, you can access the coordinate data, and then the latitude and longitude description data (so that it’s in the form of a string for our text view). These items could return empty, so I’ve added “Error loading” strings as a fall back:
Text("Latitude: \(locationDataManager.locationManager.location?.coordinate.latitude.description ?? "Error loading")")Text("Longitude: \(locationDataManager.locationManager.location?.coordinate.longitude.description ?? "Error loading")")
Your updated ContentView should look something like this:
Run your app, and you should see a pop up to use your location. I’ve selected “Allow While Using App”, and as you can see in the below screenshots (which have been blurred), it works! The SwiftUI views are being updated based on the Core Location data!
I hope that this tutorial was helpful, and can get you started on your Core Location and SwiftUI journey!
Here’s the full repository for this tutorial on GitHub for easy access:
Here’s some additional Apple Documentation links that might be helpful:
Getting the Current Location of a Device
Configuring Your App To Use Location Services
CLLocationAccuracy: The Accuracy of a Geographical Coordinate