axiom-core-location-diag

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Core Location Diagnostics

Core Location 故障排查指南

Symptom-based troubleshooting for Core Location issues.
基于症状的Core Location问题故障排查方案。

When to Use

适用场景

  • Location updates never arrive
  • Background location stops working
  • Authorization always denied
  • Location accuracy unexpectedly poor
  • Geofence events not triggering
  • Location icon won't go away
  • 从未收到位置更新
  • 后台定位停止工作
  • 始终被拒绝授权
  • 定位精度意外偏低
  • 地理围栏事件未触发
  • 定位图标无法消失

Related Skills

相关技能

  • axiom-core-location
    — Implementation patterns, decision trees
  • axiom-core-location-ref
    — API reference, code examples
  • axiom-energy-diag
    — Battery drain from location
  • axiom-mapkit-diag
    — For map-specific location display issues (Symptom 7)

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

Symptom 1: Location Updates Never Arrive

症状1:从未收到位置更新

Quick Checks

快速检查

swift
// 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")")
swift
// 1. 检查授权
let status = CLLocationManager().authorizationStatus
print("Authorization: \(status.rawValue)")
// 0=未确定, 1=受限制, 2=被拒绝, 3=始终允许, 4=使用期间允许

// 2. 检查系统级定位服务是否启用
print("Services enabled: \(CLLocationManager.locationServicesEnabled())")

// 3. 检查精度授权
let accuracy = CLLocationManager().accuracyAuthorization
print("Accuracy: \(accuracy == .fullAccuracy ? "full" : "reduced")")

Decision Tree

决策树

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
Q1: authorizationStatus返回什么?
├─ .notDetermined → 从未请求过授权
│   修复方案:添加CLServiceSession(authorization: .whenInUse)或调用requestWhenInUseAuthorization()
├─ .denied → 用户拒绝了访问
│   修复方案:显示UI说明需要定位的原因,提供跳转至设置的链接
├─ .restricted → 家长控制限制了访问
│   修复方案:告知用户,提供手动输入位置的选项
└─ .authorizedWhenInUse / .authorizedAlways → 检查下一项

Q2: locationServicesEnabled()是否返回true?
├─ NO → 系统级定位服务已禁用
│   修复方案:显示UI提示用户在设置→隐私→定位服务中启用
└─ YES → 检查下一项

Q3: 是否在遍历AsyncSequence?
├─ NO → 只有在await时才会收到更新
│   修复方案:使用Task { for try await update in CLLocationUpdate.liveUpdates() { ... } }
└─ YES → 检查下一项

Q4: Task是否已取消或损坏?
├─ YES → 位置更新到达前Task已被取消
│   修复方案:确保Task生命周期足够长(存储为属性,而非局部变量)
└─ NO → 检查下一项

Q5: 位置是否可用?(iOS 17+)
├─ 检查update.locationUnavailable
│   如果为true:设备无法确定位置(室内、飞行模式、无GPS信号)
│   修复方案:等待或告知用户移动到信号更好的位置
└─ 检查update.authorizationDenied / update.authorizationDeniedGlobally
    如果为true:优雅处理授权拒绝

Info.plist Checklist

Info.plist 检查清单

xml
<!-- Required for any location access -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your clear explanation here</string>

<!-- Required for Always authorization -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Your clear explanation here</string>
Missing these keys = silent failure with no prompt.

xml
<!-- 任何定位访问都需要 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>在此填写清晰的说明</string>

<!-- 始终授权所需 -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>在此填写清晰的说明</string>
缺少这些键会导致无提示的静默失败。

Symptom 2: Background Location Not Working

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

Quick Checks

快速检查

  1. Background mode capability: Xcode → Signing & Capabilities → Background Modes → Location updates
  2. Info.plist: Should have
    UIBackgroundModes
    with
    location
    value
  3. CLBackgroundActivitySession: Must be created AND held
  1. 后台模式权限:Xcode → 签名与功能 → 后台模式 → 勾选“位置更新”
  2. Info.plist:需包含
    UIBackgroundModes
    键,值为
    location
  3. CLBackgroundActivitySession:必须创建并持有实例

Decision Tree

决策树

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
Q1:后台模式中是否勾选了“位置更新”?
├─ NO → 后台定位被静默禁用
│   修复方案:Xcode → 签名与功能 → 后台模式 → 勾选“位置更新”
└─ YES → 检查下一项

Q2:是否持有CLBackgroundActivitySession实例?
├─ NO / 使用局部变量 → 会话被释放,后台定位停止
│   修复方案:存储为属性:var backgroundSession: CLBackgroundActivitySession?
└─ YES → 检查下一项

Q3:是否在前台启动会话?
├─ NO → 无法在后台启动新会话
│   修复方案:在应用处于前台时创建CLBackgroundActivitySession
└─ YES → 检查下一项

Q4:应用是否被终止且未恢复?
├─ YES → 重启后未重新创建会话
│   修复方案:在didFinishLaunchingWithOptions中:
│         if wasTrackingLocation {
│             backgroundSession = CLBackgroundActivitySession()
│             startLocationUpdates()
│         }
└─ NO → 检查授权级别

Q5:授权级别是什么?
├─ .authorizedWhenInUse → 配合CLBackgroundActivitySession可正常工作
│   蓝色指示器表示允许后台访问
├─ .authorizedAlways → 应正常工作,检查会话生命周期
└─ .denied → 无法进行后台定位

Common Mistakes

常见错误

swift
// ❌ 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()
}

swift
// ❌ 错误:局部变量会立即被释放
func startTracking() {
    let session = CLBackgroundActivitySession()  // 函数结束后即被销毁!
    startLocationUpdates()
}

// ✅ 正确:属性保持会话存活
var backgroundSession: CLBackgroundActivitySession?

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

Symptom 3: Authorization Always Denied

症状3:始终被拒绝授权

Decision Tree

决策树

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
Q1:是全新安装还是老用户?
├─ 全新安装且立即被拒绝 → 检查Info.plist中的描述字符串
│   缺少/为空的NSLocationWhenInUseUsageDescription会导致自动拒绝
└─ 老用户 → 检查之前的授权记录

Q2:用户之前是否拒绝过授权?
├─ YES → 用户必须在设置中手动重新启用
│   修复方案:显示UI说明定位的价值,提供打开设置的按钮:
│        UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)
└─ NO → 检查下一项

Q3:是否在错误的时机请求授权?
├─ 应用未处于“使用中”时请求 → insufficientlyInUse
│   检查:update.insufficientlyInUse或diagnostic.insufficientlyInUse
│   修复方案:仅在前台、用户交互期间请求授权
└─ NO → 检查下一项

Q4:设备是否处于受限模式?
├─ YES → 状态为.restricted(家长控制、MDM)
│   修复方案:无法覆盖限制,提供手动输入位置的选项
└─ NO → 再次检查Info.plist

Q5:Info.plist中的描述是否有说服力?
├─ 通用描述 → 用户更可能拒绝
│   反面示例:“此应用需要您的位置”
│   正面示例:“您的位置信息可帮助我们显示步行可达范围内的餐厅”
└─ 优化建议:从用户视角审视描述字符串

Info.plist String Best Practices

Info.plist描述字符串最佳实践

xml
<!-- ❌ BAD: Vague, no value proposition -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location.</string>

<!-- ✅ GOOD: Specific benefit to user -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location helps show restaurants, coffee shops, and attractions within walking distance.</string>

<!-- ❌ BAD: No explanation for Always -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need your location always.</string>

<!-- ✅ GOOD: Explains background benefit -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Enable background location to receive reminders when you arrive at saved places, even when the app is closed.</string>

xml
<!-- ❌ 错误:模糊,无价值体现 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>我们需要您的位置。</string>

<!-- ✅ 正确:明确对用户的好处 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>您的位置信息可帮助我们显示步行可达范围内的餐厅、咖啡店和景点。</string>

<!-- ❌ 错误:未说明始终授权的原因 -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>我们始终需要您的位置。</string>

<!-- ✅ 正确:说明后台定位的好处 -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>启用后台定位可在您到达保存的地点时收到提醒,即使应用已关闭。</string>

Symptom 4: Location Accuracy Unexpectedly Poor

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

Quick Checks

快速检查

swift
// 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")
    }
}
swift
// 1. 检查精度授权
let accuracy = CLLocationManager().accuracyAuthorization
print("Accuracy auth: \(accuracy == .fullAccuracy ? "full" : "reduced")")

// 2. 检查更新的精度标志(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")
    }
}

Decision Tree

决策树

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
Q1:accuracyAuthorization是什么?
├─ .reducedAccuracy → 用户选择了近似位置
│   解决方案:
│   1. 接受降低的精度(适用于天气、城市级功能)
│   2. 请求临时全精度:
│      CLServiceSession(authorization: .whenInUse, fullAccuracyPurposeKey: "Navigation")
│   3. 说明价值并跳转至设置
└─ .fullAccuracy → 检查环境和配置

Q2:位置的horizontalAccuracy是多少?
├─ < 0(通常为-1)→ 无效位置,请勿使用
│   含义:系统无法确定精度(无有效定位结果)
│   修复方案:过滤掉此类位置:guard location.horizontalAccuracy >= 0 else { continue }
│   常见场景:室内无WiFi、飞行模式、冷启动后立即请求
├─ > 100m → 可能仅使用WiFi/蜂窝网络(无GPS)
│   原因:室内、飞行模式、密集城市峡谷
│   修复方案:用户需要移动到信号更好的位置,或等待GPS锁定
├─ 10-100m → 大多数场景下的正常精度
│   如果需要更高精度:使用.automotiveNavigation或.otherNavigation配置
└─ < 10m → 良好的GPS精度
    注意:.automotiveNavigation可实现约5m的精度

Q3:使用的是哪种LiveConfiguration?
├─ .default或未设置 → 系统管理,可能优先考虑电池续航
│   如果需要更高精度:使用.fitness、.otherNavigation或.automotiveNavigation
├─ .fitness → 适用于步行活动
└─ .automotiveNavigation → 最高精度,电池消耗最大
    仅在实际导航时使用

Q4:位置是否过时?
├─ 检查location.timestamp
│   如果时间较旧:设备未移动,或更新已暂停(isStationary)
└─ 如果时间较新但精度低:环境问题

Requesting Temporary Full Accuracy (iOS 18+)

请求临时全精度(iOS 18+)

swift
// Requires Info.plist entry:
// NSLocationTemporaryUsageDescriptionDictionary
//   NavigationPurpose: "Precise location enables turn-by-turn directions"

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

swift
// 需要在Info.plist中添加:
// NSLocationTemporaryUsageDescriptionDictionary
//   NavigationPurpose: "Precise location enables turn-by-turn directions"

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

Symptom 5: Geofence Events Not Triggering

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

Quick Checks

快速检查

swift
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")
    }
}
swift
let monitor = await CLMonitor("MyMonitor")

// 1. 检查条件数量(最多20个)
let count = await monitor.identifiers.count
print("Conditions: \(count)/20")

// 2. 检查特定条件
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")
    }
}

Decision Tree

决策树

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)
Q1:正在监控多少个条件?
├─ 20个 → 达到上限,新条件会被忽略
│   修复方案:优先处理重要条件,根据用户位置动态切换
│   检查:lastEvent.conditionLimitExceeded
└─ <20个 → 检查下一项

Q2:半径是多少?
├─ <100m → 不可靠,可能无法触发
│   修复方案:使用至少100m的半径以确保可靠检测
└─ >=100m → 检查下一项

Q3:应用是否在等待monitor.events?
├─ NO → 事件未被处理,lastEvent未更新
│   修复方案:始终保持一个Task在等待:
│        for try await event in monitor.events { ... }
└─ YES → 检查下一项

Q4:应用启动时是否重新初始化了monitor?
├─ NO → 终止后监控条件丢失
│   修复方案:在didFinishLaunchingWithOptions中使用相同名称重新创建monitor
└─ YES → 检查下一项

Q5:lastEvent显示什么?
├─ state: .unknown → 系统尚未确定状态
│   等待状态确定,或检查监控是否正常工作
├─ state: .satisfied → 处于区域内,等待离开事件
├─ state: .unsatisfied → 处于区域外,等待进入事件
└─ 检查lastEvent.date → 上次更新是什么时候?
    如果时间非常旧:可能监控未正常工作

Q6:accuracyLimited是否阻止了监控?
├─ 检查:lastEvent.accuracyLimited
│   如果为true:降低的精度会阻止地理围栏功能
│   修复方案:请求全精度或接受限制
└─ NO → 检查环境(设备必须有定位权限)

Common Mistakes

常见错误

swift
// ❌ 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")
    }
}

swift
// ❌ 错误:未等待事件
let monitor = await CLMonitor("Test")
await monitor.add(condition, identifier: "Place")
// 无任何反应 - 没有Task在等待事件!

// ✅ 正确:始终等待事件
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
        }
    }
}

// ❌ 错误:创建多个同名monitor
let monitor1 = await CLMonitor("App")  // 正常
let monitor2 = await CLMonitor("App")  // 未定义行为

// ✅ 正确:每个名称对应一个monitor实例
class LocationService {
    private var monitor: CLMonitor?

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

Symptom 6: Location Icon Won't Go Away

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

Quick Checks

快速检查

The location arrow appears when:
  • App actively receiving location updates
  • CLMonitor is monitoring conditions
  • Background activity session active
定位箭头会在以下情况显示:
  • 应用正在主动接收位置更新
  • CLMonitor正在监控条件
  • 后台活动会话处于活跃状态

Decision Tree

决策树

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
Q1:应用是否仍在遍历liveUpdates?
├─ YES → 直到中断/取消才会停止更新
│   修复方案:取消Task或中断循环:
│        locationTask?.cancel()
└─ NO → 检查下一项

Q2:是否仍持有CLBackgroundActivitySession?
├─ YES → 会话保持定位访问活跃
│   修复方案:使用完成后失效:
│        backgroundSession?.invalidate()
│        backgroundSession = nil
└─ NO → 检查下一项

Q3:CLMonitor是否仍在监控条件?
├─ YES → CLMonitor使用定位进行地理围栏监控
│   注意:这是预期行为 - 图标表示监控正在进行
│   修复方案:如果确实已完成,移除所有条件:
│        for id in await monitor.identifiers {
│            await monitor.remove(id)
│        }
└─ NO → 检查下一项

Q4:旧版CLLocationManager是否仍在运行?
├─ 检查:是否调用了manager.stopUpdatingLocation()?
│   检查:是否对所有region调用了manager.stopMonitoring(for: region)?
│   修复方案:确保所有旧版API已停止
└─ NO → 检查其他使用定位的框架

Q5:其他框架是否在使用定位?
├─ MapKit的showsUserLocation = true → 会显示定位图标
│   修复方案:不需要时设置mapView.showsUserLocation = false
├─ Core Motion结合定位 → 会显示定位图标
└─ 检查所有使用定位的代码

Force Stop All Location

强制停止所有定位功能

swift
// 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)
}

swift
// 停止现代API
locationTask?.cancel()
backgroundSession?.invalidate()
backgroundSession = nil

// 移除所有CLMonitor条件
for id in await monitor.identifiers {
    await monitor.remove(id)
}

// 停止旧版API
manager.stopUpdatingLocation()
manager.stopMonitoringSignificantLocationChanges()
manager.stopMonitoringVisits()

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

Console Debugging

控制台调试

Filter Location Logs

过滤定位日志

bash
undefined
bash
undefined

View locationd logs

查看locationd日志

log stream --predicate 'subsystem == "com.apple.locationd"' --level debug
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
log stream --predicate 'subsystem == "com.apple.CoreLocation"' --level debug

Filter for specific process

过滤特定进程

log stream --predicate 'process == "YourAppName" AND subsystem == "com.apple.CoreLocation"'
undefined
log stream --predicate 'process == "YourAppName" AND subsystem == "com.apple.CoreLocation"'
undefined

Common Log Messages

常见日志消息

Log MessageMeaning
Client is not authorized
Authorization denied or not requested
Location services disabled
System-wide toggle off
Accuracy authorization is reduced
User chose approximate location
Condition limit exceeded
At 20-condition maximum
Background location access denied
Missing background capability or session

日志消息含义
Client is not authorized
授权被拒绝或未请求
Location services disabled
系统级定位服务已禁用
Accuracy authorization is reduced
用户选择了近似位置
Condition limit exceeded
达到20个条件的上限
Background location access denied
缺少后台权限或会话

Resources

资源

WWDC: 2023-10180, 2023-10147, 2024-10212
Docs: /corelocation, /corelocation/clmonitor, /corelocation/cllocationupdate
Skills: axiom-core-location, axiom-core-location-ref, axiom-energy-diag
WWDC:2023-10180, 2023-10147, 2024-10212
文档:/corelocation, /corelocation/clmonitor, /corelocation/cllocationupdate
技能:axiom-core-location, axiom-core-location-ref, axiom-energy-diag