ios-core-location
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
More from derklinke/codex-config
justfile-authoring
Create, edit, or review justfiles for the just command runner. Use when adding or modifying recipes, parameters, dependencies, settings, attributes, aliases, or shebang scripts; fixing invocation or working-directory behavior; or documenting tasks for `just --list` output.
22emotional-design-norman
Apply Don Norman's Emotional Design framework (visceral, behavioral, reflective) for UX/UI critique, product concepting, and experience polish. Use when asked to make products feel delightful, engaging, meaningful, or to balance aesthetics with usability; or when emotional design / visceral / behavioral / reflective is mentioned.
15ios-swift-concurrency
Use for Swift 6 concurrency errors and design: actor isolation, Sendable, data races, @MainActor, async/await, and thread-safety fixes
13security-best-practices
Perform language and framework specific security best-practice reviews and suggest improvements. Trigger only when the user explicitly requests security best practices guidance, a security review/report, or secure-by-default coding help. Trigger only for supported languages (python, javascript/typescript, go). Do not trigger for general code review, debugging, or non-security tasks.
7hig
Use when making design decisions, reviewing UI for HIG compliance, choosing colors/backgrounds/typography, or defending design choices - quick decision frameworks and checklists for Apple Human Interface Guidelines
2