AsyncLocationKit
📍async/await CoreLocation
Install / Use
/learn @AsyncSwift/AsyncLocationKitREADME
AsyncLocationKit
<p align="center"> <img src="https://img.shields.io/badge/Swift-5.5+-orange.svg" alt="Swift 5.5+"/> <img src="https://img.shields.io/badge/iOS-13.0+-blue.svg" alt="iOS 13.0+"/> <img src="https://img.shields.io/badge/macOS-12.0+-blue.svg" alt="macOS 12.0+"/> <img src="https://img.shields.io/badge/watchOS-6.0+-blue.svg" alt="watchOS 6.0+"/> <img src="https://img.shields.io/badge/tvOS-13.0+-blue.svg" alt="tvOS 13.0+"/> <img src="https://img.shields.io/badge/license-MIT-black.svg" alt="License: MIT"/> <img src="https://img.shields.io/badge/SPM-compatible-green.svg" alt="SPM Compatible"/> <img src="https://img.shields.io/badge/CocoaPods-compatible-green.svg" alt="CocoaPods Compatible"/> </p> <p align="center"> <b>Modern, async/await wrapper for Apple's CoreLocation framework</b><br/> No more delegate patterns or completion blocks. Embrace Swift's structured concurrency. </p>Features
- Async/Await Native: Built from the ground up for Swift's modern concurrency model
- AsyncStream Support: Monitor continuous location updates with
for awaitloops - Type-Safe: Comprehensive event types for all CoreLocation scenarios
- Zero Dependencies: Pure Swift, no external frameworks
- Multi-Platform: iOS, macOS, watchOS, and tvOS support
- Thread-Safe: Concurrent access protected with serial dispatch queues
- Swift 6 Ready: Full Sendable conformance for strict concurrency checking
Installation
Swift Package Manager
Add AsyncLocationKit to your Package.swift:
dependencies: [
.package(url: "https://github.com/AsyncSwift/AsyncLocationKit.git", from: "2.0.0")
]
Or add it directly in Xcode:
- File → Add Package Dependencies
- Enter:
https://github.com/AsyncSwift/AsyncLocationKit.git - Select version
2.0.0or later
CocoaPods
Add to your Podfile:
pod 'AsyncLocationKit', :git => 'https://github.com/AsyncSwift/AsyncLocationKit.git', :tag => '2.0.0'
Then run:
pod install
Quick Start
Initialization
Important: Always initialize
AsyncLocationManagersynchronously on the main thread.
import AsyncLocationKit
let locationManager = AsyncLocationManager(desiredAccuracy: .bestAccuracy)
Request Authorization
// Request "When In Use" authorization
let status = await locationManager.requestPermission(with: .whenInUsage)
// Or request "Always" authorization
let status = await locationManager.requestPermission(with: .always)
// Handle the authorization status
switch status {
case .authorizedWhenInUse, .authorizedAlways:
print("Location authorized!")
case .denied:
print("Location access denied")
case .restricted:
print("Location access restricted")
case .notDetermined:
print("Authorization not determined")
@unknown default:
print("Unknown authorization status")
}
Single Location Request
Get the user's location once:
do {
if let event = try await locationManager.requestLocation() {
switch event {
case .didUpdateLocations(let locations):
print("Current location: \(locations.first?.coordinate)")
case .didFailWith(let error):
print("Location error: \(error)")
default:
break
}
}
} catch {
print("Failed to get location: \(error)")
}
Continuous Location Updates
Monitor location changes using AsyncStream:
Task {
for await event in await locationManager.startUpdatingLocation() {
switch event {
case .didUpdateLocations(let locations):
print("New location: \(locations.last?.coordinate)")
case .didFailWith(let error):
print("Error: \(error)")
case .didPaused:
print("Location updates paused")
case .didResume:
print("Location updates resumed")
}
}
}
The stream automatically stops when the Task is cancelled:
let task = Task {
for await event in await locationManager.startUpdatingLocation() {
// Handle location updates
}
}
// Later, cancel the task to stop location updates
task.cancel()
Usage Examples
Monitor Authorization Changes
Task {
for await event in await locationManager.startMonitoringAuthorization() {
switch event {
case .didUpdate(let authorization):
print("Authorization changed to: \(authorization)")
}
}
}
Monitor Location Services Availability
Task {
for await event in await locationManager.startMonitoringLocationEnabled() {
switch event {
case .didUpdate(let enabled):
print("Location services enabled: \(enabled)")
}
}
}
Region Monitoring (iOS/macOS only)
let region = CLCircularRegion(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
radius: 100,
identifier: "San Francisco"
)
Task {
for await event in await locationManager.startMonitoring(for: region) {
switch event {
case .didEnterTo(let region):
print("Entered region: \(region.identifier)")
case .didExitTo(let region):
print("Exited region: \(region.identifier)")
case .didStartMonitoringFor(let region):
print("Started monitoring: \(region.identifier)")
case .monitoringDidFailFor(let region, let error):
print("Monitoring failed: \(error)")
}
}
}
Heading Updates (iOS only)
#if os(iOS)
Task {
for await event in await locationManager.startUpdatingHeading() {
switch event {
case .didUpdate(let heading):
print("Heading: \(heading.trueHeading)°")
case .didFailWith(let error):
print("Heading error: \(error)")
}
}
}
#endif
Visit Monitoring (iOS only)
#if os(iOS)
Task {
for await event in await locationManager.startMonitoringVisit() {
switch event {
case .didVisit(let visit):
print("Visit: \(visit.coordinate)")
print("Arrival: \(visit.arrivalDate)")
print("Departure: \(visit.departureDate)")
case .didFailWithError(let error):
print("Visit monitoring error: \(error)")
}
}
}
#endif
Beacon Ranging (iOS/macOS only)
#if !os(watchOS) && !os(tvOS)
let beaconConstraint = CLBeaconIdentityConstraint(
uuid: UUID(uuidString: "E2C56DB5-DFFB-48D2-B060-D0F5A71096E0")!
)
Task {
for await event in await locationManager.startRangingBeacons(satisfying: beaconConstraint) {
switch event {
case .didRange(let beacons, _):
print("Found \(beacons.count) beacons")
case .didFailRanginFor(_, let error):
print("Beacon ranging failed: \(error)")
}
}
}
#endif
Significant Location Changes (iOS/macOS only)
#if !os(watchOS) && !os(tvOS)
Task {
for await event in await locationManager.startMonitoringSignificantLocationChanges() {
switch event {
case .didUpdateLocations(let locations):
print("Significant location change: \(locations)")
case .didFailWith(let error):
print("Error: \(error)")
case .didPaused, .didResume:
break
}
}
}
#endif
Request Temporary Full Accuracy (iOS 14+)
#if os(iOS)
if #available(iOS 14.0, *) {
do {
let accuracy = try await locationManager.requestTemporaryFullAccuracyAuthorization(
purposeKey: "YourPurposeKeyFromInfoPlist"
)
print("Accuracy authorization: \(accuracy)")
} catch {
print("Failed to request full accuracy: \(error)")
}
}
#endif
Architecture
AsyncLocationKit uses a Performer Pattern to elegantly manage the complex delegate-based CoreLocation API:
┌─────────────────────────┐
│ AsyncLocationManager │
│ (Public API) │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ AsyncDelegateProxy │
│ (Event Dispatcher) │
└───────────┬─────────────┘
│
▼
┌─────────────────────────┐
│ AnyLocationPerformer │
│ (Protocol) │
└───────────┬─────────────┘
│
├──► SingleLocationUpdatePerformer
├──► MonitoringUpdateLocationPerformer
├──► AuthorizationPerformer
├──► RegionMonitoringPerformer
└──► ...and more
Key Components:
- AsyncLocationManager: Main entry point providing async/await methods
- AsyncDelegateProxy: Thread-safe dispatcher routing events to performers
- Performers: Individual handlers for specific CoreLocation delegate methods
- Events: Strongly-typed enums representing CoreLocation callbacks
This architecture provides:
- Thread-safe concurrent access
- Clean separation of concerns
- Easy testability
- Type safety throughout
Configuration Options
Location Accuracy
let manager = AsyncLocationManager(desiredAccuracy: .bestAccuracy)
// Available accuracy levels:
// .bestAccuracy
// .nearestTenMetersAccuracy
// .hundredMetersAccuracy
// .kilometerAccuracy
// .threeKilometersAccuracy
// .bestForNavigationAccuracy
// Update accuracy dynamically:
manager.updateAccuracy(with: .hundredMetersAccuracy)
Background Location Updates
let manager = AsyncLocationManager(
desiredAccuracy: .bestAccuracy,
allowsBackgroundLocationUpdates: true
)
// Update background setting dynamically (iOS/macOS/watchOS only):
#if !os(tvOS)
manager.updateAllowsBackgroundLocationUpdates(with: true)
#endif
API Reference
Location Authorization
| Method | Description | Return Type |
|--------|-------------|-------------|
| requestPermission(with:) | Request location permission | CLAuthorizationStatus |
| getAuthorizationStatus() | Get current author
Related Skills
node-connect
332.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
81.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
332.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
81.7kCommit, push, and open a PR
