axiom-core-location by charleswiltgen/axiom
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-core-location用于核心定位实现决策的规范技能。防止常见的授权错误、电池耗尽和后台定位失败。
axiom-core-location-ref — API 参考,代码示例axiom-core-location-diag — 基于症状的故障排除axiom-energy — 定位作为电池子系统错误(30-60% 拒绝率):
// 首次启动:"我们能获取始终授权吗?"
manager.requestAlwaysAuthorization()
正确(5-10% 拒绝率):
// 从使用时授权开始
CLServiceSession(authorization: .whenInUse)
// 稍后,当用户触发后台功能时:
CLServiceSession(authorization: .always)
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
时间成本:修复代码需要 15 分钟,但 30-60% 的用户永久拒绝 = 功能采用被破坏。
原因:用户会拒绝激进的请求。从最小权限开始,当用户理解价值时再升级。
错误(10 倍电池消耗):
for try await update in CLLocationUpdate.liveUpdates() {
if isNearTarget(update.location) {
triggerGeofence()
}
}
正确(系统管理,低功耗):
let monitor = await CLMonitor("Geofences")
let condition = CLMonitor.CircularGeographicCondition(
center: target, radius: 100
)
await monitor.add(condition, identifier: "Target")
for try await event in monitor.events {
if event.state == .satisfied { triggerGeofence() }
}
时间成本:重构需要 5 分钟,节省 10 倍电池电量。
错误(浪费电池):
for try await update in CLLocationUpdate.liveUpdates() {
processLocation(update.location)
// 从不停止,即使设备静止时也是如此
}
正确(自动暂停/恢复):
for try await update in CLLocationUpdate.liveUpdates() {
if let location = update.location {
processLocation(location)
}
if update.isStationary, let location = update.location {
// 设备停止移动 - 更新自动暂停
// 当设备再次移动时会恢复
saveLastKnownLocation(location)
}
}
时间成本:添加检查需要 2 分钟,显著节省电池电量。
错误(糟糕的用户体验):
for try await update in CLLocationUpdate.liveUpdates() {
guard let location = update.location else { continue }
// 用户拒绝 - 静默失败,无反馈
}
正确(优雅降级):
for try await update in CLLocationUpdate.liveUpdates() {
if update.authorizationDenied {
showManualLocationPicker()
break
}
if update.authorizationDeniedGlobally {
showSystemLocationDisabledMessage()
break
}
if let location = update.location {
processLocation(location)
}
}
时间成本:添加处理需要 10 分钟,防止用户困惑。
错误(天气应用消耗电池):
// 天气应用使用导航精度
CLLocationUpdate.liveUpdates(.automotiveNavigation)
正确(根据需求匹配精度):
// 天气:城市级别精度即可
CLLocationUpdate.liveUpdates(.default) // 或跑步者使用 .fitness
// 导航:需要高精度
CLLocationUpdate.liveUpdates(.automotiveNavigation)
| 用例 | 配置 | 精度 | 电池消耗 |
|---|---|---|---|
| 导航 | .automotiveNavigation | ~5米 | 最高 |
| 健身追踪 | .fitness | ~10米 | 高 |
| 商店查找 | .default | ~10-100米 | 中等 |
| 天气 | .default | ~100米以上 | 低 |
时间成本:更改需要 1 分钟,显著节省电池电量。
错误(电池消耗,定位图标持续显示):
func viewDidLoad() {
Task {
for try await update in CLLocationUpdate.liveUpdates() {
updateMap(update.location)
}
}
}
// 用户离开页面,更新永远持续
正确(完成后取消):
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
}
时间成本:添加取消需要 5 分钟,停止电池消耗。
错误(过程式授权处理):
func requestAuth() {
switch manager.authorizationStatus {
case .notDetermined:
manager.requestWhenInUseAuthorization()
case .authorizedWhenInUse:
if needsFullAccuracy {
manager.requestTemporaryFullAccuracyAuthorization(...)
}
// 复杂的状态机...
}
}
正确(声明式目标):
// 只需声明你需要什么 - Core Location 处理其余部分
let session = CLServiceSession(authorization: .whenInUse)
// 对于需要全精度的功能
let navSession = CLServiceSession(
authorization: .whenInUse,
fullAccuracyPurposeKey: "Navigation"
)
// 如果需要,监控诊断信息
for try await diag in session.diagnostics {
if diag.authorizationDenied { handleDenial() }
}
时间成本:迁移需要 30 分钟,代码更简单,错误更少。
Q1: 你的功能是否要求后台定位?
├─ 否 → 使用 .whenInUse
│ └─ Q2: 是否有任何功能需要精确位置?
│ ├─ 总是 → 向会话添加 fullAccuracyPurposeKey
│ └─ 有时 → 在功能激活时叠加全精度会话
│
└─ 是 → 从 .whenInUse 开始,当用户触发功能时升级到 .always
└─ Q3: 用户何时首次需要后台定位?
├─ 立即(例如,健身追踪器)→ 在第一个相关操作时请求 .always
└─ 稍后(例如,地理围栏提醒)→ 当用户创建第一个地理围栏时添加 .always 会话
Q1: 你要监控什么?
├─ 用户位置(持续追踪)
│ └─ 使用 CLLocationUpdate.liveUpdates()
│ └─ Q2: 什么活动?
│ ├─ 驾驶导航 → .automotiveNavigation
│ ├─ 步行/骑行导航 → .otherNavigation
│ ├─ 健身追踪 → .fitness
│ ├─ 飞机应用 → .airborne
│ └─ 通用 → .default 或省略
│
├─ 进入/离开区域(地理围栏)
│ └─ 使用 CLMonitor 配合 CircularGeographicCondition
│ └─ 注意:每个应用最多 20 个条件
│
├─ 信标接近度
│ └─ 使用 CLMonitor 配合 BeaconIdentityCondition
│ └─ 选择粒度:仅 UUID,UUID+主版本,UUID+主版本+次版本
│
└─ 仅显著变化(最低功耗)
└─ 使用 startMonitoringSignificantLocationChanges()(旧版)
└─ 约 500 米移动时更新,在后台工作
Q1: 使你的功能工作的最低精度是什么?
├─ 逐向导航需要 5-10 米 → .automotiveNavigation / .otherNavigation
├─ 健身追踪需要 10-20 米 → .fitness
├─ 商店查找需要 100 米 → .default
├─ 天气/城市需要 1 公里以上 → .default(可接受降低精度)
└─ 地理围栏使用系统判定 → CLMonitor 处理
Q2: 用户会快速移动吗?
├─ 驾驶(高速)→ .automotiveNavigation(为速度进行额外处理)
├─ 骑行/步行 → .otherNavigation
└─ 静止/缓慢 → .default
始终从最低可接受精度开始。更高精度 = 更高电池消耗。
背景:产品经理说"用户想要位置提醒。直接在首次启动时请求始终授权,这样就能工作。"
压力:快速发布,看起来更简单。
现实:
回应:
"当提前请求时,始终授权的拒绝率为 30-60%。我们应该从使用时授权开始,然后在用户创建第一个位置提醒时请求升级到始终授权。这样我们的拒绝率只有 5-10%,因为用户理解为什么需要它。"
证据:苹果在 WWDC 2024-10212 中的指导:"CLServiceSessions 应该被主动获取...当用户参与一个需要特殊请求的功能时,保持一个需要全精度的会话。"
背景:QA 报告"应用在后台时停止获取位置。"
压力:发布前的快速修复。
错误修复:
allowsBackgroundLocationUpdates = true正确诊断:
回应:
"后台定位需要特定设置。让我检查:(1) 后台模式能力,(2) 追踪期间持有的 CLBackgroundActivitySession,(3) 从前台启动的会话。缺少任何一项都会导致静默失败。"
检查清单:
// 1. 签名与能力 → 后台模式 → 位置更新
// 2. 持有会话引用(属性,非局部变量)
var backgroundSession: CLBackgroundActivitySession?
func startBackgroundTracking() {
// 3. 必须从前台启动
backgroundSession = CLBackgroundActivitySession()
startLocationUpdates()
}
背景:地理围栏在测试中工作,但在生产中某些用户不工作。
压力:"在我的设备上工作"的驳回。
常见原因:
回应:
"地理围栏有几个系统约束。检查:(1) 我们是否在 20 个条件限制内?(2) 所有半径是否至少 100 米?(3) 应用是否在启动时重新初始化 CLMonitor?(4) 应用是否始终等待 monitor.events?"
诊断代码:
// 检查条件数量
let count = await monitor.identifiers.count
if count >= 20 {
print("达到 20 个条件限制!")
}
// 检查所有条件
for id in await monitor.identifiers {
if let record = await monitor.record(for: id) {
let condition = record.condition
if let geo = condition as? CLMonitor.CircularGeographicCondition {
if geo.radius < 100 {
print("半径太小:\(id)")
}
}
}
}
Info.plist:
NSLocationWhenInUseUsageDescription 带有清晰的解释NSLocationAlwaysAndWhenInUseUsageDescription(清晰说明为什么需要后台)NSLocationDefaultAccuracyReducedNSLocationTemporaryUsageDescriptionDictionaryUIBackgroundModes 包含 location授权:
更新:
测试:
设置:
授权:
生命周期:
测试:
| 功能 | iOS 版本 | 备注 |
|---|---|---|
| CLLocationUpdate | iOS 17+ | AsyncSequence API |
| CLMonitor | iOS 17+ | 替代 CLCircularRegion |
| CLBackgroundActivitySession | iOS 17+ | 带蓝色指示器的后台 |
| CLServiceSession | iOS 18+ | 声明式授权 |
| 隐式服务会话 | iOS 18+ | 来自迭代 liveUpdates |
| CLLocationManager | iOS 2+ | 旧版但仍有效 |
对于 iOS 14-16 支持:使用 CLLocationManager 委托模式(参见 core-location-ref 第 7 部分)。
对于 iOS 17+:优先使用 CLLocationUpdate 和 CLMonitor。
对于 iOS 18+:添加 CLServiceSession 以实现声明式授权。
WWDC:2023-10180,2023-10147,2024-10212
文档:/corelocation,/corelocation/clmonitor,/corelocation/cllocationupdate,/corelocation/clservicesession
技能:axiom-core-location-ref,axiom-core-location-diag,axiom-energy
每周安装
91
仓库
GitHub 星标
601
首次出现
2026 年 1 月 21 日
安全审计
安装于
opencode77
claude-code71
codex71
gemini-cli70
cursor69
github-copilot66
Discipline skill for Core Location implementation decisions. Prevents common authorization mistakes, battery drain, and background location failures.
axiom-core-location-ref — API reference, code examplesaxiom-core-location-diag — Symptom-based troubleshootingaxiom-energy — Location as battery subsystemWrong (30-60% denial rate):
// First launch: "Can we have Always access?"
manager.requestAlwaysAuthorization()
Right (5-10% denial rate):
// Start with When In Use
CLServiceSession(authorization: .whenInUse)
// Later, when user triggers background feature:
CLServiceSession(authorization: .always)
Time cost : 15 min to fix code, but 30-60% of users permanently denied = feature adoption destroyed.
Why : Users deny aggressive requests. Start minimal, upgrade when user understands value.
Wrong (10x battery drain):
for try await update in CLLocationUpdate.liveUpdates() {
if isNearTarget(update.location) {
triggerGeofence()
}
}
Right (system-managed, low power):
let monitor = await CLMonitor("Geofences")
let condition = CLMonitor.CircularGeographicCondition(
center: target, radius: 100
)
await monitor.add(condition, identifier: "Target")
for try await event in monitor.events {
if event.state == .satisfied { triggerGeofence() }
}
Time cost : 5 min to refactor, saves 10x battery.
Wrong (wasted battery):
for try await update in CLLocationUpdate.liveUpdates() {
processLocation(update.location)
// Never stops, even when device stationary
}
Right (automatic pause/resume):
for try await update in CLLocationUpdate.liveUpdates() {
if let location = update.location {
processLocation(location)
}
if update.isStationary, let location = update.location {
// Device stopped moving - updates pause automatically
// Will resume when device moves again
saveLastKnownLocation(location)
}
}
Time cost : 2 min to add check, saves significant battery.
Wrong (broken UX):
for try await update in CLLocationUpdate.liveUpdates() {
guard let location = update.location else { continue }
// User denied - silent failure, no feedback
}
Right (graceful degradation):
for try await update in CLLocationUpdate.liveUpdates() {
if update.authorizationDenied {
showManualLocationPicker()
break
}
if update.authorizationDeniedGlobally {
showSystemLocationDisabledMessage()
break
}
if let location = update.location {
processLocation(location)
}
}
Time cost : 10 min to add handling, prevents confused users.
Wrong (battery drain for weather app):
// Weather app using navigation accuracy
CLLocationUpdate.liveUpdates(.automotiveNavigation)
Right (match accuracy to need):
// Weather: city-level is fine
CLLocationUpdate.liveUpdates(.default) // or .fitness for runners
// Navigation: needs high accuracy
CLLocationUpdate.liveUpdates(.automotiveNavigation)
| Use Case | Configuration | Accuracy | Battery |
|---|---|---|---|
| Navigation | .automotiveNavigation | ~5m | Highest |
| Fitness tracking | .fitness | ~10m | High |
| Store finder | .default | ~10-100m | Medium |
| Weather | .default | ~100m+ | Low |
Time cost : 1 min to change, significant battery savings.
Wrong (battery drain, location icon persists):
func viewDidLoad() {
Task {
for try await update in CLLocationUpdate.liveUpdates() {
updateMap(update.location)
}
}
}
// User navigates away, updates continue forever
Right (cancel when done):
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
}
Time cost : 5 min to add cancellation, stops battery drain.
Wrong (procedural authorization juggling):
func requestAuth() {
switch manager.authorizationStatus {
case .notDetermined:
manager.requestWhenInUseAuthorization()
case .authorizedWhenInUse:
if needsFullAccuracy {
manager.requestTemporaryFullAccuracyAuthorization(...)
}
// Complex state machine...
}
}
Right (declarative goals):
// Just declare what you need - Core Location handles the rest
let session = CLServiceSession(authorization: .whenInUse)
// For feature needing full accuracy
let navSession = CLServiceSession(
authorization: .whenInUse,
fullAccuracyPurposeKey: "Navigation"
)
// Monitor diagnostics if needed
for try await diag in session.diagnostics {
if diag.authorizationDenied { handleDenial() }
}
Time cost : 30 min to migrate, simpler code, fewer bugs.
Q1: Does your feature REQUIRE background location?
├─ NO → Use .whenInUse
│ └─ Q2: Does any feature need precise location?
│ ├─ ALWAYS → Add fullAccuracyPurposeKey to session
│ └─ SOMETIMES → Layer full-accuracy session when feature active
│
└─ YES → Start with .whenInUse, upgrade to .always when user triggers feature
└─ Q3: When does user first need background location?
├─ IMMEDIATELY (e.g., fitness tracker) → Request .always on first relevant action
└─ LATER (e.g., geofence reminders) → Add .always session when user creates first geofence
Q1: What are you monitoring for?
├─ USER POSITION (continuous tracking)
│ └─ Use CLLocationUpdate.liveUpdates()
│ └─ Q2: What activity?
│ ├─ Driving navigation → .automotiveNavigation
│ ├─ Walking/cycling nav → .otherNavigation
│ ├─ Fitness tracking → .fitness
│ ├─ Airplane apps → .airborne
│ └─ General → .default or omit
│
├─ ENTRY/EXIT REGIONS (geofencing)
│ └─ Use CLMonitor with CircularGeographicCondition
│ └─ Note: Maximum 20 conditions per app
│
├─ BEACON PROXIMITY
│ └─ Use CLMonitor with BeaconIdentityCondition
│ └─ Choose granularity: UUID only, UUID+major, UUID+major+minor
│
└─ SIGNIFICANT CHANGES ONLY (lowest power)
└─ Use startMonitoringSignificantLocationChanges() (legacy)
└─ Updates ~500m movements, works in background
Q1: What's the minimum accuracy that makes your feature work?
├─ TURN-BY-TURN NAV needs 5-10m → .automotiveNavigation / .otherNavigation
├─ FITNESS TRACKING needs 10-20m → .fitness
├─ STORE FINDER needs 100m → .default
├─ WEATHER/CITY needs 1km+ → .default (reduced accuracy acceptable)
└─ GEOFENCING uses system determination → CLMonitor handles it
Q2: Will user be moving fast?
├─ DRIVING (high speed) → .automotiveNavigation (extra processing for speed)
├─ CYCLING/WALKING → .otherNavigation
└─ STATIONARY/SLOW → .default
Always start with lowest acceptable accuracy. Higher accuracy = higher battery drain.
Context : PM says "Users want location reminders. Just request Always access on first launch so it works."
Pressure : Ship fast, seems simpler.
Reality :
Response :
"Always authorization has 30-60% denial rates when requested upfront. We should start with When In Use, then request Always upgrade when the user creates their first location reminder. This gives us a 5-10% denial rate because users understand why they need it."
Evidence : Apple's own guidance in WWDC 2024-10212: "CLServiceSessions should be taken proactively... hold one requiring full-accuracy when people engage a feature that would warrant a special ask for it."
Context : QA reports "App stops getting location when backgrounded."
Pressure : Quick fix before release.
Wrong fixes :
allowsBackgroundLocationUpdates = true without understandingRight diagnosis :
Response :
"Background location requires specific setup. Let me check: (1) Background mode capability, (2) CLBackgroundActivitySession held during tracking, (3) session started from foreground. Missing any of these causes silent failure."
Checklist :
// 1. Signing & Capabilities → Background Modes → Location updates
// 2. Hold session reference (property, not local variable)
var backgroundSession: CLBackgroundActivitySession?
func startBackgroundTracking() {
// 3. Must start from foreground
backgroundSession = CLBackgroundActivitySession()
startLocationUpdates()
}
Context : Geofences work in testing but not in production for some users.
Pressure : "It works on my device" dismissal.
Common causes :
Response :
"Geofencing has several system constraints. Check: (1) Are we within the 20-condition limit? (2) Are all radii at least 100m? (3) Is the app reinitializing CLMonitor on launch? (4) Is the app always awaiting on monitor.events?"
Diagnostic code :
// Check condition count
let count = await monitor.identifiers.count
if count >= 20 {
print("At 20-condition limit!")
}
// Check all conditions
for id in await monitor.identifiers {
if let record = await monitor.record(for: id) {
let condition = record.condition
if let geo = condition as? CLMonitor.CircularGeographicCondition {
if geo.radius < 100 {
print("Radius too small: \(id)")
}
}
}
}
Info.plist :
NSLocationWhenInUseUsageDescription with clear explanationNSLocationAlwaysAndWhenInUseUsageDescription if using Always (clear why background needed)NSLocationDefaultAccuracyReduced if reduced accuracy acceptableNSLocationTemporaryUsageDescriptionDictionary if requesting temporary full accuracyUIBackgroundModes includes location if background trackingAuthorization :
Updates :
Testing :
Setup :
Authorization :
Lifecycle :
Testing :
| Feature | iOS Version | Notes |
|---|---|---|
| CLLocationUpdate | iOS 17+ | AsyncSequence API |
| CLMonitor | iOS 17+ | Replaces CLCircularRegion |
| CLBackgroundActivitySession | iOS 17+ | Background with blue indicator |
| CLServiceSession | iOS 18+ | Declarative authorization |
| Implicit service sessions | iOS 18+ | From iterating liveUpdates |
| CLLocationManager | iOS 2+ | Legacy but still works |
For iOS 14-16 support : Use CLLocationManager delegate pattern (see core-location-ref Part 7).
For iOS 17+ : Prefer CLLocationUpdate and CLMonitor.
For iOS 18+ : Add CLServiceSession for declarative authorization.
WWDC : 2023-10180, 2023-10147, 2024-10212
Docs : /corelocation, /corelocation/clmonitor, /corelocation/cllocationupdate, /corelocation/clservicesession
Skills : axiom-core-location-ref, axiom-core-location-diag, axiom-energy
Weekly Installs
91
Repository
GitHub Stars
601
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode77
claude-code71
codex71
gemini-cli70
cursor69
github-copilot66
Swift Actor 线程安全持久化:构建离线优先应用的编译器强制安全数据层
1,700 周安装