Skip to content

Instantly share code, notes, and snippets.

@runys
Last active May 13, 2025 08:25
Show Gist options
  • Save runys/10a01deb2b7182c674823b2d051ad271 to your computer and use it in GitHub Desktop.
Save runys/10a01deb2b7182c674823b2d051ad271 to your computer and use it in GitHub Desktop.
Location Manager service providing an asynchronous way of accessing the user current location.
import Foundation
import CoreLocation
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
}
//MARK: Request Authorization to access the User Location
func checkAuthorization() {
switch locationManager.authorizationStatus {
case .notDetermined:
locationManager.requestWhenInUseAuthorization()
default:
return
}
}
//MARK: Continuation Object for the User Location
private var continuation: CheckedContinuation<CLLocation, Error>?
// Error messages associated with the location manager
enum LocationManagerError: String, Error {
case replaceContinuation = "Continuation replaced."
case locationNotFound = "No location found."
}
//MARK: Async Request the Current Location
var currentLocation: CLLocation {
get async throws {
// Check if there is a continuation being worked on
if self.continuation != nil {
// If so, resumes it throwing an error
self.continuation?.resume(throwing: LocationManagerError.replaceContinuation)
// And deletes it, so a new one can be created
self.continuation = nil
}
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()
}
}
}
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
} else {
// If there is no location, resumes the continuation throwing and error to avoid a continuation leak
continuation?.resume(throwing: LocationManagerError.locationNotFound)
}
}
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
}
}
import SwiftUI
import MapKit
struct ContentView: View {
// MARK: Instance of the location manager
var locationManager = LocationManager()
// MARK: Properties
@State var location: CLLocation?
// MARK: Position of the MapCamera
@State private var position: MapCameraPosition = .automatic
// MARK: Body
var body: some View {
VStack {
Map(position: $position) {
UserAnnotation()
}
.clipShape(RoundedRectangle(cornerRadius: 15))
.shadow(radius: 5)
Text(location?.description ?? "No location yet")
.padding()
.foregroundColor(.secondary)
Button {
Task { await self.updateLocation() }
} label: {
Text("Get Location")
}
}
.padding()
.background {
Color.gray.opacity(0.4)
.ignoresSafeArea()
}
.task {
// 1. Check if the app is authorized to access the location services of the device
locationManager.checkAuthorization()
}
}
// MARK: Get the current user location if available
func updateLocation() async {
do {
// 1. Get the current location from the location manager
self.location = try await locationManager.currentLocation
// 2. Update the camera position of the map to center around the user location
self.updateMapPosition()
} catch {
print("Could not get user location: \(error.localizedDescription)")
}
}
// MARK: Change the camera of the Map view
func updateMapPosition() {
if let location = self.location {
let regionCenter = CLLocationCoordinate2D(
latitude: location.coordinate.latitude,
longitude: location.coordinate.longitude
)
let regionSpan = MKCoordinateSpan(latitudeDelta: 0.125, longitudeDelta: 0.125)
self.position = .region(MKCoordinateRegion(center: regionCenter, span: regionSpan))
}
}
}
@KavinduDissanayake
Copy link

this has SWIFT TASK CONTINUATION MISUSE: currentLocation leaked its continuation!

@runys
Copy link
Author

runys commented Jan 16, 2025

Hey, thanks for letting me know! Did you get this error from Xcode? This code is a bit old, I'll test it again and fix it.

@theedov
Copy link

theedov commented May 12, 2025

Had this error "SWIFT TASK CONTINUATION MISUSE: currentLocation leaked its continuation without resuming it. This may cause tasks waiting on it to remain suspended forever." in my code so was searching how other people do it. Looks like your code has the same issue.

@runys
Copy link
Author

runys commented May 13, 2025

Solved! I've updated the location manager so a continuation is always resumed, in every code path. Thanks for the feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment