How to Get Current Location Coordinates in Core Location

How to Get Current Location Coordinates in Core Location

Written by

in

Quick answer: To get user coordinates, add the NSLocationWhenInUseUsageDescription key to your Info.plist, keep a strong reference to CLLocationManager, set its delegate, and call requestWhenInUseAuthorization(). Once authorized, call requestLocation() for a single update or startUpdatingLocation() for continuous updates, then read coordinates in locationManager(_:didUpdateLocations:).

Why this happens

Accessing device coordinates requires explicit privacy authorization and an active system lifecycle listener. Developers frequently encounter silent failures where delegate methods never fire, coordinates remain nil, or the app crashes immediately upon startup.

First, iOS strictly requires a privacy rationale string in your application target’s configuration. If your app requests location authorization without an explanation key defined in Info.plist, the operating system terminates the process instantly without presenting an alert.

Second, CLLocationManager instances must be held with a strong reference. Instantiating a location manager inside a local function scope causes ARC (Automatic Reference Counting) to deallocate the manager before it can process hardware callbacks or present authorization dialogs. In addition, calling requestLocation() without implementing the mandatory failure delegate method causes an unhandled runtime error.

How to fix it

1. Add privacy usage descriptions to Info.plist

Before executing any Swift code, configure your project’s property list to describe why your application requires location access.

  1. Select your project in the Xcode Project Navigator.
  2. Choose your app target and open the Info tab (or open Info.plist directly).
  3. Add the key Privacy – Location When In Use Usage Description (NSLocationWhenInUseUsageDescription).
  4. Set a clear, user-facing string value explaining why the app needs access, such as: "We need your location to display nearby services on the map."

Avoid requesting NSLocationAlwaysAndWhenInUseUsageDescription unless your app delivers true background location tracking (e.g., turn-by-turn navigation). App Store Review will reject apps requesting background permissions without active background modes enabled.

2. Request a one-time coordinate with requestLocation()

If you only need to grab the user’s location once (for example, to center a map view or find nearby stores), use requestLocation() instead of continuous tracking. This saves battery and automatically powers down the GPS hardware once resolved.

Create a dedicated controller or manager class with a strong reference to CLLocationManager:

import UIKit
import CoreLocation

final class LocationService: NSObject, CLLocationManagerDelegate {
    private let locationManager = CLLocationManager()

    override init() {
        super.init()
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
    }

    func checkAuthorizationAndFetch() {
        switch locationManager.authorizationStatus {
        case .notDetermined:
            locationManager.requestWhenInUseAuthorization()
        case .authorizedWhenInUse, .authorizedAlways:
            locationManager.requestLocation()
        case .denied, .restricted:
            print("Location access denied by user or parental controls.")
        @unknown default:
            break
        }
    }

    // Handle authorization changes (iOS 14+)
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .authorizedWhenInUse, .authorizedAlways:
            manager.requestLocation()
        case .denied, .restricted:
            print("User declined authorization.")
        default:
            break
        }
    }

    // Required: Handles successful location updates
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let latestLocation = locations.last else {
            return
        }

        let coordinate = latestLocation.coordinate
        print("Latitude: \(coordinate.latitude), Longitude: \(coordinate.longitude)")
    }

    // Required when using requestLocation(): Must be implemented to prevent runtime crash
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Failed to retrieve location: \(error.localizedDescription)")
    }
}

3. Handle continuous coordinate updates

If your application tracks real-time movement, use startUpdatingLocation(). Ensure you set an appropriate desiredAccuracy and stop updates as soon as continuous tracking is no longer needed.

import CoreLocation

final class TrackingManager: NSObject, CLLocationManagerDelegate {
    private let locationManager = CLLocationManager()

    func startTracking() {
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.distanceFilter = 10.0 // Updates every 10 meters moved

        if locationManager.authorizationStatus == .notDetermined {
            locationManager.requestWhenInUseAuthorization()
        } else if locationManager.authorizationStatus == .authorizedWhenInUse {
            locationManager.startUpdatingLocation()
        }
    }

    func stopTracking() {
        locationManager.stopUpdatingLocation()
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        for location in locations {
            // Filter out cached points that are older than 15 seconds
            guard abs(location.timestamp.timeIntervalSinceNow) < 15 else { continue }

            print("Continuous coordinate: \(location.coordinate.latitude), \(location.coordinate.longitude)")
        }
    }

    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Tracking failed: \(error.localizedDescription)")
    }
}

4. Modern Swift async/await with CLLocationUpdate (iOS 17+)

In modern Swift codebases targeting iOS 17 or newer, you can bypass the traditional delegate pattern entirely by streaming locations using the async sequence CLLocationUpdate.liveUpdates():

import CoreLocation

@MainActor
final class AsyncLocationProvider {
    func streamCoordinates() async {
        let manager = CLLocationManager()
        if manager.authorizationStatus == .notDetermined {
            manager.requestWhenInUseAuthorization()
        }

        do {
            let updates = CLLocationUpdate.liveUpdates()
            for try await update in updates {
                guard let location = update.location else { continue }
                print("Async coordinate: \(location.coordinate.latitude), \(location.coordinate.longitude)")

                // Break the loop if only one reading is required
                break
            }
        } catch {
            print("Location stream failed: \(error.localizedDescription)")
        }
    }
}

How to prevent it

  • Store your CLLocationManager instance as a strong property on a long-lived class (e.g., an ObservableObject, service singleton, or view controller) rather than inside a transient function.
  • Always implement locationManager(_:didFailWithError:) whenever you use CLLocationManagerDelegate. Core Location will trigger a runtime assertion failure if requestLocation() encounters an error and this method is missing.
  • Stop location updates using stopUpdatingLocation() when your view disappears or tracking is complete to avoid heavy battery consumption.
  • Discard locations with negative horizontal accuracy (location.horizontalAccuracy < 0), which signals an invalid GPS reading.
  • Test location callbacks on the iOS Simulator by going to Features > Location in the simulator menu bar and selecting Custom Location or City Run.

FAQ

Why does locationManager(_:didUpdateLocations:) never get called?

Check that your CLLocationManager is held by a strong class reference rather than a local variable that gets deallocated immediately. In addition, verify that the simulator or physical device has Location Services enabled and that you implemented locationManagerDidChangeAuthorization(_:) to trigger updates once permissions are granted.

What is the difference between requestLocation() and startUpdatingLocation()?

requestLocation() requests a single reading, delivers it to the delegate, and automatically turns off the location hardware. startUpdatingLocation() continuously monitors and delivers coordinates until you explicitly invoke stopUpdatingLocation().

Can I get location coordinates in the background?

Yes, but you must request requestAlwaysAuthorization(), configure the NSLocationAlwaysAndWhenInUseUsageDescription key in Info.plist, and enable the Location updates option under Signing & Capabilities > Background Modes in Xcode.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 4.0; this article is shared under the same license.