Core Location Patterns
Implementation discipline for location features: correct authorization, battery-aware tracking, and reliable background behavior.
Related Skills
- references/api-reference.md - API reference
Anti-Patterns (and cost)
1) Premature Always authorization
// Wrong
manager.requestAlwaysAuthorization()
// Right
CLServiceSession(authorization: .whenInUse)
// upgrade to .always only when user starts background feature
- Cost: fast code, poor adoption (high denial rates)
- Rule: start minimal, escalate with contextual user intent
2) Continuous updates for geofencing
// Wrong: polling location to emulate geofencing
for try await update in CLLocationUpdate.liveUpdates() { ... }
// Right: system geofencing
let monitor = await CLMonitor("Geofences")
await monitor.add(CLMonitor.CircularGeographicCondition(center: target, radius: 100), identifier: "Target")
for try await event in monitor.events { ... }
- Cost: major battery waste
- Rule: use
CLMonitorfor entry/exit semantics
3) Ignoring stationary behavior
for try await update in CLLocationUpdate.liveUpdates() {
if let location = update.location { processLocation(location) }
if update.isStationary { saveLastKnownLocation(update.location) }
}
- Cost: unnecessary processing while stationary
- Rule: respect stationary/pause behavior
4) No graceful denial path
for try await update in CLLocationUpdate.liveUpdates() {
if update.authorizationDenied { showManualLocationPicker(); break }
if update.authorizationDeniedGlobally { showSystemLocationDisabledMessage(); break }
if let location = update.location { processLocation(location) }
}
- Cost: silent failure + broken UX
- Rule: always branch explicit denial states
5) Wrong accuracy for use case
// Wrong for weather app
CLLocationUpdate.liveUpdates(.automotiveNavigation)
// Better by intent
CLLocationUpdate.liveUpdates(.default) // weather/store-finder
CLLocationUpdate.liveUpdates(.fitness) // activity
CLLocationUpdate.liveUpdates(.automotiveNavigation) // turn-by-turn
| Use case | Config | Typical accuracy | Battery |
|---|---|---|---|
| Navigation | .automotiveNavigation | ~5m | Highest |
| Fitness | .fitness | ~10m | High |
| Store finder | .default | ~10-100m | Medium |
| Weather | .default | city-level | Low |
6) Not stopping updates
private var locationTask: Task<Void, Error>?
func startTracking() {
locationTask = Task {
for try await update in CLLocationUpdate.liveUpdates() {
if Task.isCancelled { break }
updateMap(update.location)
}
}
}
func stopTracking() {
locationTask?.cancel()
locationTask = nil
}
- Cost: battery drain + persistent location indicator
- Rule: explicit start/stop lifecycle
7) Ignoring CLServiceSession (iOS 18+)
let session = CLServiceSession(authorization: .whenInUse)
let navSession = CLServiceSession(
authorization: .whenInUse,
fullAccuracyPurposeKey: "Navigation"
)
for try await diag in session.diagnostics {
if diag.authorizationDenied { handleDenial() }
}
- Cost: manual state machine complexity
- Rule: declare needs; observe diagnostics
Decision Trees
Authorization
- Need background location?
- no ->
.whenInUse - yes -> start
.whenInUse, upgrade.alwaysat first background feature action
- Need precise location?
- always -> full accuracy purpose key
- sometimes -> temporary full-accuracy session only during relevant feature
Monitoring mode
- Track continuous position?
- use
CLLocationUpdate.liveUpdates()with matchingLiveConfiguration
- Entry/exit geofence?
- use
CLMonitor.CircularGeographicCondition(20-condition cap)
- Beacon proximity?
- use
CLMonitor.BeaconIdentityConditionwith needed granularity
- Low-power broad movement only?
- use significant-change monitoring (legacy API)
Accuracy selection
- pick lowest accuracy that still satisfies feature requirements
- high speed driving ->
.automotiveNavigation - walking/cycling ->
.otherNavigation - general/stationary ->
.default
Higher accuracy always increases energy cost.
Pressure Scenarios
"Request Always on first launch"
Response:
- upfront Always requests commonly increase denial
- use staged ask:
.whenInUsefirst, upgrade on feature trigger - reference CLServiceSession guidance for contextual asks
"Background location not working"
Checklist:
- background capability present
CLBackgroundActivitySessionretained- session started in foreground
- relaunch recovery restores session + update stream
"Geofence unreliable in production"
Checklist:
- <=20 conditions
- radius >=100m
- always await
monitor.events - recreate monitor on app launch
- inspect
lastEventandaccuracyLimited
Pre-Release Checklist
Info.plist
- [ ]
NSLocationWhenInUseUsageDescription - [ ]
NSLocationAlwaysAndWhenInUseUsageDescription(if applicable) - [ ]
NSLocationDefaultAccuracyReduced(if acceptable) - [ ]
NSLocationTemporaryUsageDescriptionDictionary(if requesting temporary full accuracy) - [ ]
UIBackgroundModesincludeslocation(if background tracking)
Authorization and UX
- [ ] Start with minimal auth
- [ ] Upgrade only at feature trigger
- [ ] Denial and global-disable paths handled
- [ ] Reduced-accuracy path tested
Runtime lifecycle
- [ ] Appropriate
LiveConfiguration - [ ] Stationary behavior handled
- [ ] Tasks canceled when inactive
- [ ] Geofencing not implemented via continuous polling
Testing
- [ ] Deny/re-enable flow tested
- [ ] Background/foreground transitions tested
- [ ] Termination + relaunch recovery tested
Background Location Checklist
Setup
- [ ] Capability enabled: Location updates
- [ ]
CLBackgroundActivitySessionretained strongly - [ ] Created in foreground
- [ ] Relaunch path rehydrates tracking state
Lifecycle
- [ ] Persist "tracking active" state
- [ ] Restore session and update stream on launch
- [ ] Reinitialize
CLMonitorwith same name
Version Guidance
| Feature | Minimum iOS | Note |
|---|---:|---|
| CLLocationUpdate | 17 | AsyncSequence API |
| CLMonitor | 17 | Region/beacon monitoring |
| CLBackgroundActivitySession | 17 | Background with indicator |
| CLServiceSession | 18 | Declarative authorization |
- iOS 14-16: use
CLLocationManagerdelegate style. - iOS 17+: prefer
CLLocationUpdateandCLMonitor. - iOS 18+: add
CLServiceSession.
Resources
- WWDC: 2023-10180, 2023-10147, 2024-10212
- Docs:
/corelocation,/corelocation/clmonitor,/corelocation/cllocationupdate,/corelocation/clservicesession - Reference: references/api-reference.md
Scan to join WeChat group