Loading...
Loading...
Reference skill for Apple's CoreLocation framework in Swift/SwiftUI. Use this skill whenever the user works with location services, GPS, geofencing, beacon ranging, geocoding, compass headings, or any CLLocationManager-related code on iOS, macOS, watchOS, or visionOS. Trigger on mentions of: CoreLocation, CLLocationManager, CLLocation, location permissions, geofencing, CLMonitor, iBeacon, CLGeocoder, reverse geocoding, background location updates, "When In Use" / "Always" authorization, CLLocationUpdate, live updates, significant location changes, or any location-related Info.plist keys like NSLocationWhenInUseUsageDescription.
npx skill4agent add ios-agent/iosagent.dev apple-corelocation| Topic | Reference File | When to Read |
|---|---|---|
| Live Updates & async/await patterns | | SwiftUI apps, async location streams, background activity sessions |
| Authorization & Permissions | | Permission flows, Info.plist keys, authorization status handling |
| Region Monitoring & CLMonitor | | Geofencing, condition monitoring, circular regions |
| Geocoding | | Address ↔ coordinate conversion, reverse geocoding, CLPlacemark |
| iBeacon & Compass | | Beacon ranging, heading updates, magnetometer |
| Background Location | | Background updates, CLBackgroundActivitySession, power optimization |
| CLLocationManager API | | Full property/method reference for CLLocationManager |
import CoreLocation
let updates = CLLocationUpdate.liveUpdates()
for try await update in updates {
if let location = update.location {
// Process location
print("Lat: \(location.coordinate.latitude), Lon: \(location.coordinate.longitude)")
}
if update.authorizationDenied {
// Handle denied authorization
}
if update.authorizationRequestInProgress {
// System is showing the authorization dialog
}
}.notDeterminedrequestWhenInUseAuthorization()// High accuracy (GPS, more power)
let updates = CLLocationUpdate.liveUpdates(.default)
// Power-efficient options
let updates = CLLocationUpdate.liveUpdates(.automotiveNavigation)
let updates = CLLocationUpdate.liveUpdates(.otherNavigation)
let updates = CLLocationUpdate.liveUpdates(.fitness)
let updates = CLLocationUpdate.liveUpdates(.airborne)location: CLLocation?isStationary: BoolauthorizationDenied: BoolauthorizationDeniedGlobally: BoolauthorizationRequestInProgress: BoolinsufficientlyInUse: BoollocationUnavailable: BoolaccuracyLimited: Bool@MainActor
class LocationsHandler: ObservableObject {
static let shared = LocationsHandler()
private let manager: CLLocationManager
private var background: CLBackgroundActivitySession?
@Published var lastLocation = CLLocation()
@Published var isStationary = false
@Published var updatesStarted: Bool = UserDefaults.standard.bool(forKey: "liveUpdatesStarted") {
didSet { UserDefaults.standard.set(updatesStarted, forKey: "liveUpdatesStarted") }
}
private init() {
self.manager = CLLocationManager()
}
func startLocationUpdates() {
if self.manager.authorizationStatus == .notDetermined {
self.manager.requestWhenInUseAuthorization()
}
Task {
do {
self.updatesStarted = true
let updates = CLLocationUpdate.liveUpdates()
for try await update in updates {
if !self.updatesStarted { break }
if let loc = update.location {
self.lastLocation = loc
self.isStationary = update.isStationary
}
}
} catch {
print("Could not start location updates")
}
}
}
func stopLocationUpdates() {
self.updatesStarted = false
}
}| Key | When to Use |
|---|---|
| App uses location while in foreground |
| App needs location in background too |
| Request reduced accuracy by default |
CLAuthorizationStatus| Value | Meaning |
|---|---|
| User hasn't been asked yet |
| App cannot use location (e.g., parental controls) |
| User explicitly denied |
| App can use location while in foreground |
| App can use location at any time |
let manager = CLLocationManager()
// For foreground-only access
manager.requestWhenInUseAuthorization()
// For background access (after getting "When In Use" first)
manager.requestAlwaysAuthorization()
// For temporary full accuracy (when user granted reduced accuracy)
manager.requestTemporaryFullAccuracyAuthorization(withPurposeKey: "MyPurposeKey")let monitor = await CLMonitor("myMonitor")
// Add a circular geographic condition
await monitor.add(
CLMonitor.CircularGeographicCondition(center: coordinate, radius: 200),
identifier: "coffee-shop"
)
// Observe events
for try await event in await monitor.events {
switch event.state {
case .satisfied:
print("Entered region: \(event.identifier)")
case .unsatisfied:
print("Exited region: \(event.identifier)")
default:
break
}
}let geocoder = CLGeocoder()
// Reverse geocode: coordinate → address
geocoder.reverseGeocodeLocation(location) { placemarks, error in
if let placemark = placemarks?.first {
print(placemark.locality ?? "Unknown city")
}
}
// Forward geocode: address → coordinate
geocoder.geocodeAddressString("1 Apple Park Way, Cupertino") { placemarks, error in
if let location = placemarks?.first?.location {
print(location.coordinate)
}
}| Property | Type | Description |
|---|---|---|
| | Latitude and longitude (WGS 84) |
| | Meters above sea level |
| | Accuracy in meters (negative = invalid) |
| | Altitude accuracy in meters |
| | Meters per second |
| | Degrees relative to true north |
| | When the location was determined |
| | Floor of a building, if available |
| | Info about the location source |
startMonitoringVisits()startMonitoringSignificantLocationChanges()startUpdatingLocation()desiredAccuracyCLLocationUpdate.liveUpdates()| Constant | Description |
|---|---|
| Highest precision, most power |
| Best available accuracy |
| Within ~10 meters |
| Within ~100 meters |
| Within ~1 km |
| Within ~3 km |
| Deliberately reduced accuracy |
CLBackgroundActivitySessionisAuthorizedForWidgetUpdateshorizontalAccuracypausesLocationUpdatesAutomaticallyactivityTypeallowsBackgroundLocationUpdates = true