
Updating the User’s Location with Core Location and Swift Concurrency in SwiftUI
Learn how to create an asynchronous API to access Core Location on a SwiftUI app.
You can create your own wrappers to manage access to iOS core services, taking advantage of the features of the Swift language to create modern and flexible APIs for your application.
One of the services you can work with is Core Location, using Swift Concurrency to create a simple and elegant approach to accessing the user location. With a CheckedContinuation object, we can interface between synchronous and asynchronous code, so we can update the location of the user once the location manager can retrieve it using delegation.
Creating the Location Manager
Create a LocationManager
class that inherits from the NSObject
class and conforms to the CLLocationManagerDelegate
protocol. Add a private property called locationManager
, a CLLocationManager
object that will provide us access to the location services of the device.
Also, override the default initializer so we can assign the delegate to the locationManager
object to be the LocationManager
class.
class LocationManager: NSObject, CLLocationManagerDelegate {
//MARK: Object to Access Location Services
private let locationManager = CLLocationManager()
//MARK: Set up the Location Manager Delegate
override init() {
super.init()
locationManager.delegate = self
}
}
Declaration of the LocationManager class
Requesting Authorization
To be able to access the current location of the user you must ask for authorization to use the location services of the device.

Apple Developer Documentation for requesting authorization to use location services
On the project settings, you must go into the Info area and add to the Custom iOS Target properties the key Privacy - Location When In Use Usage Description
and a string value with a message explaining why your application needs access to the location services.

With that done, create a short method to check if the location manager's authorization is not determined and request it if that’s the case.
class LocationManager: NSObject, CLLocationManagerDelegate {
...
//MARK: Request Authorization to access the User Location
func checkAuthorization() {
switch locationManager.authorizationStatus {
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
default:
return
}
}
}
Method to request user authorization to access the location services
Getting the Current Location
Now to get the user's current location create a property that will store an CheckedContinuation
object that, when resumed, returns an CLLocation
object or throws an error.
class LocationManager: NSObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
...
//MARK: Continuation Object for the User Location
private var continuation: CheckedContinuation<CLLocation, Error>?
}
Setting up the continuation object that will return the user location once it is available
Create a computed property named currentLocation
that will asynchronously return the result of the continuation object once it is resolved. If everything goes well we will receive an CLLocation
object, if not an error will be thrown.
The continuation
property is set with the continuation object provided by the withCheckedThrowingContinuation(function:_:)
method in its body closure, and then we use the locationManager to request a one-time delivery of the user’s current location.
class LocationManager: NSObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
...
private var continuation: CheckedContinuation<CLLocation, Error>?
//MARK: Asynchronously request the current location
var currentLocation: CLLocation {
get async throws {
return try await withCheckedThrowingContinuation { continuation in
// 1. Set up the continuation object
self.continuation = continuation
// 2. Triggers the update of the current location
locationManager.requestLocation()
}
}
}
}
Property that returns the current location of the user asynchronously
The last step is to implement the CLLocationManagerDelegate
methods that are triggered when the requestLocation()
method is called. Provide an implementation for a successful case, resuming the continuation object returning the current user’s location, and a case where it fails to provide the user’s location, resuming the continuation object by throwing the error object associated with it.
class LocationManager: NSObject, CLLocationManagerDelegate {
...
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// 4. If there is a location available
if let lastLocation = locations.last {
// 5. Resumes the continuation object with the user location as result
continuation?.resume(returning: lastLocation)
// Resets the continuation object
continuation = nil
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
// 6. If not possible to retrieve a location, resumes with an error
continuation?.resume(throwing: error)
// Resets the continuation object
continuation = nil
}
}
Location manager delegate methods to retrieve the user location
To see an example of how to use the location manager in a SwiftUI view to display the current user location on a Map check the following code snippet on GitHub.

Example of a SwiftUI View that uses the Location Manager created on this reference article
To see the latest updates on MapKit with SwiftUI check our article highlighting the updates made on WWDC in 2023.
