mcpField
A
axiom-core-location-diag

axiom-core-location-diag

github.com/charleswiltgen开发1.2k175 次安装今天更新
代码仓库

关于 axiom-core-location-diag

面向 Apple 平台开发者的 Core Location 故障排查技能,按症状提供检查项、决策流程和修复建议,涵盖定位更新、后台定位、授权、精度及地理围栏。

主要功能

  1. 01按症状定位故障原因
  2. 02检查授权与配置
  3. 03排查后台定位问题
  4. 04诊断精度和地理围栏
  5. 05使用系统日志调试

适用场景

  1. 01定位更新未到达时排查原因
  2. 02后台定位停止工作时检查配置
  3. 03地理围栏事件未触发时诊断
  4. 04定位图标持续显示时停止活动

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-core-location-diag

安装后在 Claude Code 中输入/axiom-core-location-diag确认已加载。

详细介绍

Core Location 诊断

基于症状排查 Core Location 问题。

适用场景

  • 始终收不到位置更新
  • 后台定位停止工作
  • 授权始终被拒绝
  • 定位精度意外偏低
  • 地理围栏事件未触发
  • 定位图标无法消失

相关技能

  • axiom-core-location — 实现模式、决策树
  • axiom-core-location-ref — API 参考、代码示例
  • axiom-energy-diag — 定位导致的电池耗电
  • axiom-mapkit-diag — 地图特定的位置显示问题(症状 7)

症状 1:始终收不到位置更新

快速检查
// 1. Check authorization
let status = CLLocationManager().authorizationStatus
print("Authorization: \(status.rawValue)")
// 0=notDetermined, 1=restricted, 2=denied, 3=authorizedAlways, 4=authorizedWhenInUse

// 2. Check if location services enabled system-wide
print("Services enabled: \(CLLocationManager.locationServicesEnabled())")

// 3. Check accuracy authorization
let accuracy = CLLocationManager().accuracyAuthorization
print("Accuracy: \(accuracy == .fullAccuracy ? "full" : "reduced")")
决策树
Q1: What does authorizationStatus return?
├─ .notDetermined → Authorization never requested
│   Fix: Add CLServiceSession(authorization: .whenInUse) or requestWhenInUseAuthorization()
│
├─ .denied → User denied access
│   Fix: Show UI explaining why location needed, link to Settings
│
├─ .restricted → Parental controls block access
│   Fix: Inform user, offer manual location input
│
└─ .authorizedWhenInUse / .authorizedAlways → Check next

Q2: Is locationServicesEnabled() returning true?
├─ NO → Location services disabled system-wide
│   Fix: Show UI prompting user to enable in Settings → Privacy → Location Services
│
└─ YES → Check next

Q3: Are you iterating the AsyncSequence?
├─ NO → Updates only arrive when you await
│   Fix: Task { for try await update in CLLocationUpdate.liveUpdates() { ... } }
│
└─ YES → Check next

Q4: Is the Task cancelled or broken?
├─ YES → Task cancelled before updates arrived
│   Fix: Ensure Task lives long enough (store in property, not local)
│
└─ NO → Check next

Q5: Is location available? (iOS 17+)
├─ Check update.locationUnavailable
│   If true: Device cannot determine location (indoors, airplane mode, no GPS)
│   Fix: Wait or inform user to move to better location
│
└─ Check update.authorizationDenied / update.authorizationDeniedGlobally
    If true: Handle denial gracefully
Info.plist 检查清单

<key>NSLocationWhenInUseUsageDescription</key>
<string>Your clear explanation here</string>


<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Your clear explanation here</string>

缺少这些键会导致静默失败,且不会显示提示。


症状 2:后台定位无法正常工作

快速检查
  1. 后台模式功能:Xcode → Signing & Capabilities → Background Modes → Location updates
  2. Info.plist:应包含值为 location 的 UIBackgroundModes
  3. CLBackgroundActivitySession:必须创建并持有
决策树
Q1: Is "Location updates" checked in Background Modes?
├─ NO → Background location silently disabled
│   Fix: Xcode → Signing & Capabilities → Background Modes → Location updates
│
└─ YES → Check next

Q2: Are you holding CLBackgroundActivitySession?
├─ NO / Using local variable → Session deallocates, background stops
│   Fix: Store in property: var backgroundSession: CLBackgroundActivitySession?
│
└─ YES → Check next

Q3: Was session started from foreground?
├─ NO → Cannot start new session from background
│   Fix: Create CLBackgroundActivitySession while app in foreground
│
└─ YES → Check next

Q4: Is app being terminated and not recovering?
├─ YES → Not recreating session on relaunch
│   Fix: In didFinishLaunchingWithOptions:
│         if wasTrackingLocation {
│             backgroundSession = CLBackgroundActivitySession()
│             startLocationUpdates()
│         }
│
└─ NO → Check authorization level

Q5: What is authorization level?
├─ .authorizedWhenInUse → This is fine with CLBackgroundActivitySession
│   The blue indicator allows background access
│
├─ .authorizedAlways → Should work, check session lifecycle
│
└─ .denied → No background access possible
常见错误
// ❌ WRONG: Local variable deallocates immediately
func startTracking() {
    let session = CLBackgroundActivitySession()  // Dies at end of function!
    startLocationUpdates()
}

// ✅ RIGHT: Property keeps session alive
var backgroundSession: CLBackgroundActivitySession?

func startTracking() {
    backgroundSession = CLBackgroundActivitySession()
    startLocationUpdates()
}

症状 3:授权始终被拒绝

决策树
Q1: Is this a fresh install or returning user?
├─ FRESH INSTALL with immediate denial → Check Info.plist strings
│   Missing/empty NSLocationWhenInUseUsageDescription = automatic denial
│
└─ RETURNING USER → Check previous denial

Q2: Did user previously deny?
├─ YES → User must manually re-enable in Settings
│   Fix: Show UI explaining value, with button to open Settings:
│        UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
│
└─ NO → Check next

Q3: Are you requesting authorization at wrong time?
├─ Requesting when app not "in use" → insufficientlyInUse
│   Check: update.insufficientlyInUse or diagnostic.insufficientlyInUse
│   Fix: Only request authorization from foreground, during user interaction
│
└─ NO → Check next

Q4: Is device in restricted mode?
├─ YES → .restricted status (parental controls, MDM)
│   Fix: Cannot override. Offer manual location input.
│
└─ NO → Check Info.plist again

Q5: Are Info.plist strings compelling?
├─ Generic string → Users more likely to deny
│   Bad: "This app needs your location"
│   Good: "Your location helps us show restaurants within walking distance"
│
└─ Review: Look at string from user's perspective
Info.plist 文案最佳实践

<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location.</string>


<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location helps show restaurants, coffee shops, and attractions within walking distance.</string>


<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need your location always.</string>


<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Enable background location to receive reminders when you arrive at saved places, even when the app is closed.</string>

症状 4:定位精度意外偏低

快速检查
// 1. Check accuracy authorization
let accuracy = CLLocationManager().accuracyAuthorization
print("Accuracy auth: \(accuracy == .fullAccuracy ? "full" : "reduced")")

// 2. Check update's accuracy flag (iOS 17+)
for try await update in CLLocationUpdate.liveUpdates() {
    if update.accuracyLimited {
        print("Accuracy limited - updates every 15-20 min")
    }
    if let location = update.location {
        print("Horizontal accuracy: \(location.horizontalAccuracy)m")
    }
}
决策树
Q1: What is accuracyAuthorization?
├─ .reducedAccuracy → User chose approximate location
│   Options:
│   1. Accept reduced accuracy (weather, city-level features)
│   2. Request temporary full accuracy:
│      CLServiceSession(authorization: .whenInUse, fullAccuracyPurposeKey: "Navigation")
│   3. Explain value and link to Settings
│
└─ .fullAccuracy → Check environment and configuration

Q2: What is horizontalAccuracy on locations?
├─ < 0 (typically -1) → INVALID location, do not use
│   Meaning: System could not determine accuracy (no valid fix)
│   Fix: Filter out: guard location.horizontalAccuracy >= 0 else { continue }
│   Common when: Indoors with no WiFi, airplane mode, immediately after cold start
│
├─ > 100m → Likely using WiFi/cell only (no GPS)
│   Causes: Indoors, airplane mode, dense urban canyon
│   Fix: User needs to move to better location, or wait for GPS lock
│
├─ 10-100m → Normal for most use cases
│   If need better: Use .automotiveNavigation or .otherNavigation config
│
└─ < 10m → Good GPS accuracy
    Note: .automotiveNavigation can achieve ~5m

Q3: What LiveConfiguration are you using?
├─ .default or none → System manages, may prioritize battery
│   If need more accuracy: Use .fitness, .otherNavigation, or .automotiveNavigation
│
├─ .fitness → Good for pedestrian activities
│
└─ .automotiveNavigation → Highest accuracy, axiom-highest battery
    Only use for actual navigation

Q4: Is the location stale?
├─ Check location.timestamp
│   If old: Device hasn't moved, or updates paused (isStationary)
│
└─ If timestamp recent but accuracy poor: Environmental issue
请求临时完整精度(iOS 18+)
// Requires Info.plist entry:
// NSLocationTemporaryUsageDescriptionDictionary
//   NavigationPurpose: "Precise location enables turn-by-turn directions"

let session = CLServiceSession(
    authorization: .whenInUse,
    fullAccuracyPurposeKey: "NavigationPurpose"
)

症状 5:地理围栏事件未触发

快速检查
let monitor = await CLMonitor("MyMonitor")

// 1. Check condition count (max 20)
let count = await monitor.identifiers.count
print("Conditions: \(count)/20")

// 2. Check specific condition
if let record = await monitor.record(for: "MyGeofence") {
    let lastEvent = record.lastEvent
    print("State: \(lastEvent.state)")
    print("Date: \(lastEvent.date)")

    if let geo = record.condition as? CLMonitor.CircularGeographicCondition {
        print("Center: \(geo.center)")
        print("Radius: \(geo.radius)m")
    }
}
决策树
Q1: How many conditions are monitored?
├─ 20 → At the limit, new conditions ignored
│   Fix: Prioritize important conditions, swap dynamically based on user location
│   Check: lastEvent.conditionLimitExceeded
│
└─ < 20 → Check next

Q2: What is the radius?
├─ < 100m → Unreliable, may not trigger
│   Fix: Use minimum 100m radius for reliable detection
│
└─ >= 100m → Check next

Q3: Is the app awaiting monitor.events?
├─ NO → Events not processed, lastEvent not updated
│   Fix: Always have a Task awaiting:
│        for try await event in monitor.events { ... }
│
└─ YES → Check next

Q4: Was monitor reinitialized on app launch?
├─ NO → Monitor conditions lost after termination
│   Fix: Recreate monitor with same name in didFinishLaunchingWithOptions
│
└─ YES → Check next

Q5: What does lastEvent show?
├─ state: .unknown → System hasn't determined state yet
│   Wait for determination, or check if monitoring is working
│
├─ state: .satisfied → Inside region, waiting for exit
│
├─ state: .unsatisfied → Outside region, waiting for entry
│
└─ Check lastEvent.date → When was last update?
    If very old: May not be monitoring correctly

Q6: Is accuracyLimited preventing monitoring?
├─ Check: lastEvent.accuracyLimited
│   If true: Reduced accuracy prevents geofencing
│   Fix: Request full accuracy or accept limitation
│
└─ NO → Check environment (device must have location access)
常见错误
// ❌ WRONG: Not awaiting events
let monitor = await CLMonitor("Test")
await monitor.add(condition, identifier: "Place")
// Nothing happens - no Task awaiting events!

// ✅ RIGHT: Always await events
let monitor = await CLMonitor("Test")
await monitor.add(condition, identifier: "Place")

Task {
    for try await event in monitor.events {
        switch event.state {
        case .satisfied: handleEntry(event.identifier)
        case .unsatisfied: handleExit(event.identifier)
        case .unknown: break
        @unknown default: break
        }
    }
}

// ❌ WRONG: Creating multiple monitors with same name
let monitor1 = await CLMonitor("App")  // OK
let monitor2 = await CLMonitor("App")  // UNDEFINED BEHAVIOR

// ✅ RIGHT: One monitor instance per name
class LocationService {
    private var monitor: CLMonitor?

    func setup() async {
        monitor = await CLMonitor("App")
    }
}

症状 6:定位图标无法消失

快速检查

出现定位箭头的情况:

  • App 正在主动接收位置更新
  • CLMonitor 正在监控条件
  • 后台活动会话处于活动状态
决策树
Q1: Is your app still iterating liveUpdates?
├─ YES → Updates continue until you break/cancel
│   Fix: Cancel the Task or break from loop:
│        locationTask?.cancel()
│
└─ NO → Check next

Q2: Is CLBackgroundActivitySession still held?
├─ YES → Session keeps location access active
│   Fix: Invalidate when done:
│        backgroundSession?.invalidate()
│        backgroundSession = nil
│
└─ NO → Check next

Q3: Is CLMonitor still monitoring conditions?
├─ YES → CLMonitor uses location for geofencing
│   Note: This is expected behavior - icon shows monitoring active
│   Fix: If truly done, remove all conditions:
│        for id in await monitor.identifiers {
│            await monitor.remove(id)
│        }
│
└─ NO → Check next

Q4: Is legacy CLLocationManager still running?
├─ Check: manager.stopUpdatingLocation() called?
│   Check: manager.stopMonitoring(for: region) for all regions?
│   Fix: Ensure all legacy APIs stopped
│
└─ NO → Check other location-using frameworks

Q5: Other frameworks using location?
├─ MapKit with showsUserLocation = true → Shows location
│   Fix: mapView.showsUserLocation = false when not needed
│
├─ Core Motion with location → Shows location
│
└─ Check all location-using code
强制停止所有定位
// Stop modern APIs
locationTask?.cancel()
backgroundSession?.invalidate()
backgroundSession = nil

// Remove all CLMonitor conditions
for id in await monitor.identifiers {
    await monitor.remove(id)
}

// Stop legacy APIs
manager.stopUpdatingLocation()
manager.stopMonitoringSignificantLocationChanges()
manager.stopMonitoringVisits()

for region in manager.monitoredRegions {
    manager.stopMonitoring(for: region)
}

控制台调试

筛选定位日志
# View locationd logs
log stream --predicate 'subsystem == "com.apple.locationd"' --level debug

# View your app's location-related logs
log stream --predicate 'subsystem == "com.apple.CoreLocation"' --level debug

# Filter for specific process
log stream --predicate 'process == "YourAppName" AND subsystem == "com.apple.CoreLocation"'
常见日志消息
日志消息含义
Client is not authorized授权已被拒绝或尚未请求
Location services disabled系统级开关已关闭
Accuracy authorization is reduced用户选择了近似位置
Condition limit exceeded已达到 20 个条件的上限
Background location access denied缺少后台功能或会话

资源

WWDC:2023-10180、2023-10147、2024-10212

文档:/corelocation、/corelocation/clmonitor、/corelocation/cllocationupdate

技能:axiom-core-location、axiom-core-location-ref、axiom-energy-diag

评论

支持 Markdown