mcpField
A
axiom-core-location-ref

axiom-core-location-ref

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

关于 axiom-core-location-ref

面向使用 Core Location 的 iOS 开发者的 API 参考,覆盖 iOS 17+ 的现代接口以及旧版模式。内容包括位置更新、地理围栏、授权和后台定位配置。

主要功能

  1. 01现代位置更新 API
  2. 02地理围栏与信标监控
  3. 03授权和精确度管理
  4. 04后台位置更新配置
  5. 05旧版 API 参考

适用场景

  1. 01实现地理围栏或区域监控
  2. 02配置后台位置跟踪
  3. 03排查定位授权与服务问题

安装

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

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

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

详细介绍

Core Location 参考

现代 Core Location 的完整 API 参考(iOS 17+)。

适用场景

  • 需要查询 CLLocationUpdate、CLMonitor、CLServiceSession 的 API 签名
  • 实现地理围栏或区域监控
  • 配置后台位置更新
  • 了解授权模式
  • 调试位置服务问题

相关技能

  • axiom-core-location — 反模式、决策树、压力场景
  • axiom-core-location-diag — 基于症状的问题排查
  • axiom-energy-ref — 将位置服务视为电池子系统(精度与功耗)

第 1 部分:现代 API 概述(iOS 17+)

四个关键类取代了旧版 CLLocationManager 模式:

类用途iOS
CLLocationUpdate用于位置更新的 AsyncSequence17+
CLMonitor基于条件的地理围栏/信标监控17+
CLServiceSession声明式授权目标18+
CLBackgroundActivitySession后台位置支持17+

迁移路径:旧版 CLLocationManager 仍可使用,但新 API 提供:

  • Swift 并发(async/await)
  • 自动暂停/恢复
  • 简化的授权
  • 更高的电池效率

第 2 部分:CLLocationUpdate API

基本用法
import CoreLocation

Task {
    do {
        for try await update in CLLocationUpdate.liveUpdates() {
            if let location = update.location {
                // Process location
            }
            if update.isStationary {
                break // Stop when user stops moving
            }
        }
    } catch {
        // Handle location errors
    }
}
LiveConfiguration 选项
CLLocationUpdate.liveUpdates(.default)
CLLocationUpdate.liveUpdates(.automotiveNavigation)
CLLocationUpdate.liveUpdates(.otherNavigation)
CLLocationUpdate.liveUpdates(.fitness)
CLLocationUpdate.liveUpdates(.airborne)

根据使用场景选择。如果不确定,请使用 .default 或省略参数。

关键属性
属性类型说明
locationCLLocation?当前位置(不可用时为 nil)
isStationaryBool设备停止移动时为 True
authorizationDeniedBool用户拒绝了位置访问权限
authorizationDeniedGloballyBool系统范围内的位置服务已停用
authorizationRequestInProgressBool正在等待用户决定是否授权
accuracyLimitedBool精度降低(每 15-20 分钟更新一次)
locationUnavailableBool无法确定位置
insufficientlyInUseBool无法请求授权(应用不在前台)
自动暂停/恢复

设备静止时:

  1. 最后一次更新会以 isStationary = true 和有效的 location 送达
  2. 更新暂停(节省电量)
  3. 设备移动后,更新会以 isStationary = false 恢复

无需采取任何操作——此过程会自动发生。

AsyncSequence 操作
// Get first location with speed > 10 m/s
let fastUpdate = try await CLLocationUpdate.liveUpdates()
    .first { $0.location?.speed ?? 0 > 10 }

// WARNING: Avoid filters that may never match (e.g., horizontalAccuracy < 1)

第 3 部分:CLMonitor API

用于监控地理条件和信标的 Swift actor。

基本地理围栏
let monitor = await CLMonitor("MyMonitor")

// Add circular region
let condition = CLMonitor.CircularGeographicCondition(
    center: CLLocationCoordinate2D(latitude: 37.33, longitude: -122.01),
    radius: 100
)
await monitor.add(condition, identifier: "ApplePark")

// Await events
for try await event in monitor.events {
    switch event.state {
    case .satisfied:  // User entered region
        handleEntry(event.identifier)
    case .unsatisfied:  // User exited region
        handleExit(event.identifier)
    case .unknown:
        break
    @unknown default:
        break
    }
}
CircularGeographicCondition
CLMonitor.CircularGeographicCondition(
    center: CLLocationCoordinate2D,
    radius: CLLocationDistance  // meters, minimum ~100m effective
)
BeaconIdentityCondition

三个粒度级别:

// All beacons with UUID (any site)
CLMonitor.BeaconIdentityCondition(uuid: myUUID)

// Specific site (UUID + major)
CLMonitor.BeaconIdentityCondition(uuid: myUUID, major: 100)

// Specific beacon (UUID + major + minor)
CLMonitor.BeaconIdentityCondition(uuid: myUUID, major: 100, minor: 5)
条件数量限制

每个应用最多 20 个条件。 请优先选择要监控的对象。如有需要,可根据用户位置动态切换区域。

添加时指定假定状态
// If you know initial state
await monitor.add(condition, identifier: "Work", assuming: .unsatisfied)

如果假设有误,Core Location 会进行更正。

访问记录
// Get single record
if let record = await monitor.record(for: "ApplePark") {
    let condition = record.condition
    let lastEvent = record.lastEvent
    let state = lastEvent.state
    let date = lastEvent.date
}

// Get all identifiers
let allIds = await monitor.identifiers
事件属性
属性说明
identifier条件的字符串标识符
state.satisfied、.unsatisfied、.unknown
date状态变更时间
refinement对于通配符信标,表示检测到的实际 UUID/major/minor
conditionLimitExceeded条件过多(最多 20 个)
conditionUnsupported条件类型不可用
accuracyLimited精度降低,无法进行监控
关键要求
  1. 每个名称只能有一个监视器——同一时间只能有一个使用指定名称的实例
  2. 始终等待事件——只有在处理事件后,它才会成为 lastEvent
  3. 启动时重新初始化——在 didFinishLaunchingWithOptions 中重新创建监视器

第 4 部分:CLServiceSession API(iOS 18+)

声明式授权——告知 Core Location 你需要什么,而不是要它怎么做。

基本用法
// Hold session for duration of feature
let session = CLServiceSession(authorization: .whenInUse)

for try await update in CLLocationUpdate.liveUpdates() {
    // Process updates
}
授权要求
CLServiceSession(authorization: .none)       // No auth request
CLServiceSession(authorization: .whenInUse)  // Request When In Use
CLServiceSession(authorization: .always)     // Request Always (must start in foreground)
请求完整精度
// For features requiring precise location (e.g., navigation)
CLServiceSession(
    authorization: .whenInUse,
    fullAccuracyPurposeKey: "NavigationPurpose"  // Key in Info.plist
)

需要在 Info.plist 中配置 NSLocationTemporaryUsageDescriptionDictionary。

隐式会话

迭代 CLLocationUpdate.liveUpdates() 或 CLMonitor.events 会创建目标为 .whenInUse 的隐式会话。

若要禁用隐式会话:


<key>NSLocationRequireExplicitServiceSession</key>
<true/>
会话叠加

不要替换会话,而要叠加会话:

// Base session for app
let baseSession = CLServiceSession(authorization: .whenInUse)

// Additional session when navigation feature active
let navSession = CLServiceSession(
    authorization: .whenInUse,
    fullAccuracyPurposeKey: "Nav"
)
// Both sessions active simultaneously
诊断属性
for try await diagnostic in session.diagnostics {
    if diagnostic.authorizationDenied {
        // User denied—offer alternative
    }
    if diagnostic.authorizationDeniedGlobally {
        // Location services off system-wide
    }
    if diagnostic.insufficientlyInUse {
        // Can't request auth (not foreground)
    }
    if diagnostic.alwaysAuthorizationDenied {
        // Always auth specifically denied
    }
    if !diagnostic.authorizationRequestInProgress {
        // Decision made (granted or denied)
        break
    }
}
会话生命周期

会话会在以下情况下持续存在:

  • App 进入后台
  • App 暂停
  • App 终止(由 Core Location 跟踪)

重新启动时,在 didFinishLaunchingWithOptions 中立即重新创建会话。


第 5 部分:授权状态机

授权级别
状态说明
.notDetermined用户尚未作出决定
.restricted家长控制限制了访问
.denied用户明确拒绝
.authorizedWhenInUseApp 处于活动状态时允许访问
.authorizedAlways允许在后台访问
精度授权
值说明
.fullAccuracy精确位置
.reducedAccuracy大致位置(约 5km),每 15-20 分钟更新一次
必需的 Info.plist 键

<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to show nearby places</string>


<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We track your location to send arrival reminders</string>


<key>NSLocationDefaultAccuracyReduced</key>
<true/>
旧版授权模式
@MainActor
class LocationManager: NSObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()

    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        case .notDetermined:
            manager.requestWhenInUseAuthorization()
        case .authorizedWhenInUse, .authorizedAlways:
            enableLocationFeatures()
        case .denied, .restricted:
            disableLocationFeatures()
        @unknown default:
            break
        }
    }
}

第 6 部分:后台位置

要求
  1. 后台模式功能:Signing & Capabilities → Background Modes → Location updates
  2. Info.plist:添加包含 location 值的 UIBackgroundModes
  3. CLBackgroundActivitySession 或 LiveActivity
CLBackgroundActivitySession
// Create and HOLD reference (deallocation invalidates session)
var backgroundSession: CLBackgroundActivitySession?

func startBackgroundTracking() {
    // Must start from foreground
    backgroundSession = CLBackgroundActivitySession()

    Task {
        for try await update in CLLocationUpdate.liveUpdates() {
            processUpdate(update)
        }
    }
}

func stopBackgroundTracking() {
    backgroundSession?.invalidate()
    backgroundSession = nil
}
后台指示器

以下情况下会显示蓝色状态栏/胶囊:

  • App 的授权为“使用 App 期间”
  • App 在后台接收位置
  • CLBackgroundActivitySession 处于活动状态
App 生命周期
  1. 前台 → 后台:会话继续运行
  2. 后台 → 暂停:会话保留,更新暂停
  3. 暂停 → 终止:Core Location 跟踪会话
  4. 终止 → 后台启动:立即重新创建会话
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Recreate background session if was tracking
    if wasTrackingLocation {
        backgroundSession = CLBackgroundActivitySession()
        startLocationUpdates()
    }
    return true
}

第 7 部分:旧版 API(iOS 12-16)

CLLocationManager 代理模式
class LocationManager: NSObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()

    override init() {
        super.init()
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        manager.distanceFilter = 10 // meters
    }

    func startUpdates() {
        manager.startUpdatingLocation()
    }

    func stopUpdates() {
        manager.stopUpdatingLocation()
    }

    func locationManager(_ manager: CLLocationManager,
                        didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        // Process location
    }
}
精度常量
常量精度电池影响
kCLLocationAccuracyBestForNavigation约 5m最高
kCLLocationAccuracyBest约 10m很高
kCLLocationAccuracyNearestTenMeters约 10m高
kCLLocationAccuracyHundredMeters约 100m中
kCLLocationAccuracyKilometer约 1km低
kCLLocationAccuracyThreeKilometers约 3km很低
kCLLocationAccuracyReduced约 5km最低
旧版区域监控
// Deprecated in iOS 17, use CLMonitor instead
let region = CLCircularRegion(
    center: coordinate,
    radius: 100,
    identifier: "MyRegion"
)
region.notifyOnEntry = true
region.notifyOnExit = true
manager.startMonitoring(for: region)
重大位置变化

用于粗略跟踪的低功耗替代方案:

manager.startMonitoringSignificantLocationChanges()
// Updates ~500m movements, works in background
到访监控

检测到达/离开:

manager.startMonitoringVisits()

func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
    let arrival = visit.arrivalDate
    let departure = visit.departureDate
    let coordinate = visit.coordinate
}

第 8 部分:地理围栏最佳实践

区域大小
  • 有效半径下限:约 100 米
  • 较小区域:可能无法可靠触发
  • 较大区域:更可靠,但精度较低
20 个区域限制策略
// Dynamic region management
func updateMonitoredRegions(userLocation: CLLocation) async {
    let nearbyPOIs = fetchNearbyPOIs(around: userLocation, limit: 20)

    // Remove old regions
    for id in await monitor.identifiers {
        if !nearbyPOIs.contains(where: { $0.id == id }) {
            await monitor.remove(id)
        }
    }

    // Add new regions
    for poi in nearbyPOIs {
        let condition = CLMonitor.CircularGeographicCondition(
            center: poi.coordinate,
            radius: 100
        )
        await monitor.add(condition, identifier: poi.id)
    }
}
进入/退出时间
  • 进入:通常在几秒到几分钟内
  • 退出:离开后可能需要 3-5 分钟
  • 精度取决于:蜂窝基站、WiFi、GPS 的可用性
持久性
  • 条件会在多次启动 App 之间持续存在
  • 启动时必须使用相同名称重新初始化监控器
  • Core Location 会因事件唤醒 App

第 9 部分:测试与模拟

Xcode 位置模拟
  1. 在模拟器中运行
  2. Debug → Simulate Location → 选择位置
  3. 或使用自定义 GPX 文件
自定义 GPX 路线
<?xml version="1.0"?>
<gpx version="1.1">
    <wpt lat="37.331686" lon="-122.030656">
        <time>2024-01-01T00:00:00Z</time>
    </wpt>
    <wpt lat="37.332686" lon="-122.031656">
        <time>2024-01-01T00:00:10Z</time>
    </wpt>
</gpx>
测试授权状态

Settings → Privacy & Security → Location Services:

  • 切换 App 的授权状态
  • 切换系统范围的位置服务
  • 测试降低的精度
控制台筛选
# Filter location logs
log stream --predicate 'subsystem == "com.apple.locationd"'

第 10 部分:Swift 并发集成

任务取消
let locationTask = Task {
    for try await update in CLLocationUpdate.liveUpdates() {
        if Task.isCancelled { break }
        processUpdate(update)
    }
}

// Later
locationTask.cancel()
MainActor 注意事项
@MainActor
class LocationViewModel: ObservableObject {
    @Published var currentLocation: CLLocation?

    func startTracking() {
        Task {
            for try await update in CLLocationUpdate.liveUpdates() {
                // Already on MainActor, safe to update @Published
                self.currentLocation = update.location
            }
        }
    }
}
错误处理
Task {
    do {
        for try await update in CLLocationUpdate.liveUpdates() {
            if update.authorizationDenied {
                throw LocationError.authorizationDenied
            }
            processUpdate(update)
        }
    } catch {
        handleError(error)
    }
}

第 11 部分:地理编码

CLGeocoder — 正向地理编码(地址 → 坐标)
let geocoder = CLGeocoder()

func geocodeAddress(_ address: String) async throws -> CLLocation? {
    let placemarks = try await geocoder.geocodeAddressString(address)
    return placemarks.first?.location
}

// With locale for localized results
let placemarks = try await geocoder.geocodeAddressString(
    "1 Apple Park Way",
    in: nil,  // CLRegion hint (optional)
    preferredLocale: Locale(identifier: "en_US")
)
CLGeocoder — 反向地理编码(坐标 → 地址)
func reverseGeocode(_ location: CLLocation) async throws -> CLPlacemark? {
    let placemarks = try await geocoder.reverseGeocodeLocation(location)
    return placemarks.first
}

// Usage
if let placemark = try await reverseGeocode(location) {
    let street = placemark.thoroughfare          // "Apple Park Way"
    let city = placemark.locality                // "Cupertino"
    let state = placemark.administrativeArea     // "CA"
    let zip = placemark.postalCode               // "95014"
    let country = placemark.country              // "United States"
    let isoCountry = placemark.isoCountryCode    // "US"
}
CLPlacemark 关键属性
属性示例说明
name"Apple Park"位置名称
thoroughfare"Apple Park Way"街道名称
subThoroughfare"1"门牌号
locality"Cupertino"城市
subLocality"Silicon Valley"街区
administrativeArea"CA"州/省
postalCode"95014"邮政编码
country"United States"国家名称
isoCountryCode"US"ISO 国家代码
timeZoneAmerica/Los_Angeles时区
locationCLLocation坐标
地理编码速率限制
  • 一次只能发起一个请求 — 如果已有请求正在进行,CLGeocoder 会抛出异常
  • Apple 会进行速率限制 — 限制请求频率,避免出现 kCLErrorGeocodeCanceled
  • 缓存结果 — 不要对相同的地址/坐标重复进行地理编码
  • 谨慎批量处理 — 在连续的地理编码请求之间添加延迟
// Check if geocoder is busy
if geocoder.isGeocoding {
    geocoder.cancelGeocode()  // Cancel previous before starting new
}

故障排除速查

症状检查项
没有位置更新授权状态、Info.plist 键
后台功能不起作用后台模式功能、CLBackgroundActivitySession
始终授权未生效使用 .always 的 CLServiceSession,并在前台启动
地理围栏未触发区域数量(最多 20 个)、半径(最小约 100m)
仅有降低精度的定位检查 accuracyAuthorization,请求临时完整精度
位置图标一直显示确保调用 stopUpdatingLocation(),或退出异步循环

资源

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

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

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

评论

支持 Markdown