axiom-core-location-ref

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Core Location Reference

Core Location 参考文档

Comprehensive API reference for modern Core Location (iOS 17+).
适用于iOS 17及以上版本的现代Core Location完整API参考文档。

When to Use

适用场景

  • Need API signatures for CLLocationUpdate, CLMonitor, CLServiceSession
  • Implementing geofencing or region monitoring
  • Configuring background location updates
  • Understanding authorization patterns
  • Debugging location service issues
  • 需要CLLocationUpdate、CLMonitor、CLServiceSession的API签名
  • 实现地理围栏或区域监控功能
  • 配置后台定位更新
  • 理解授权模式
  • 调试定位服务相关问题

Related Skills

相关技能

  • axiom-core-location
    — Anti-patterns, decision trees, pressure scenarios
  • axiom-core-location-diag
    — Symptom-based troubleshooting
  • axiom-energy-ref
    — Location as battery subsystem (accuracy vs power)

  • axiom-core-location
    — 反模式、决策树、压力场景
  • axiom-core-location-diag
    — 基于症状的故障排除
  • axiom-energy-ref
    — 定位作为电池子系统(精度与功耗的平衡)

Part 1: Modern API Overview (iOS 17+)

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

Four key classes replace legacy CLLocationManager patterns:
ClassPurposeiOS
CLLocationUpdate
AsyncSequence for location updates17+
CLMonitor
Condition-based geofencing/beacons17+
CLServiceSession
Declarative authorization goals18+
CLBackgroundActivitySession
Background location support17+
Migration path: Legacy CLLocationManager still works, but new APIs provide:
  • Swift concurrency (async/await)
  • Automatic pause/resume
  • Simplified authorization
  • Better battery efficiency

四个核心类替代了传统的CLLocationManager模式:
用途iOS版本
CLLocationUpdate
用于定位更新的AsyncSequence17+
CLMonitor
基于条件的地理围栏/信标监控17+
CLServiceSession
声明式授权目标18+
CLBackgroundActivitySession
后台定位支持17+
迁移路径:传统的CLLocationManager仍可使用,但新API具备以下优势:
  • Swift并发(async/await)
  • 自动暂停/恢复
  • 简化的授权流程
  • 更优的电池效率

Part 2: CLLocationUpdate API

第二部分:CLLocationUpdate API

Basic Usage

基础用法

swift
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
    }
}
swift
import CoreLocation

Task {
    do {
        for try await update in CLLocationUpdate.liveUpdates() {
            if let location = update.location {
                // 处理定位信息
            }
            if update.isStationary {
                break // 当用户停止移动时停止更新
            }
        }
    } catch {
        // 处理定位错误
    }
}

LiveConfiguration Options

LiveConfiguration 选项

swift
CLLocationUpdate.liveUpdates(.default)
CLLocationUpdate.liveUpdates(.automotiveNavigation)
CLLocationUpdate.liveUpdates(.otherNavigation)
CLLocationUpdate.liveUpdates(.fitness)
CLLocationUpdate.liveUpdates(.airborne)
Choose based on use case. If unsure, use
.default
or omit parameter.
swift
CLLocationUpdate.liveUpdates(.default)
CLLocationUpdate.liveUpdates(.automotiveNavigation)
CLLocationUpdate.liveUpdates(.otherNavigation)
CLLocationUpdate.liveUpdates(.fitness)
CLLocationUpdate.liveUpdates(.airborne)
根据使用场景选择对应的选项。若不确定,使用
.default
或省略参数。

Key Properties

核心属性

PropertyTypeDescription
location
CLLocation?
Current location (nil if unavailable)
isStationary
Bool
True when device stopped moving
authorizationDenied
Bool
User denied location access
authorizationDeniedGlobally
Bool
Location services disabled system-wide
authorizationRequestInProgress
Bool
Awaiting user authorization decision
accuracyLimited
Bool
Reduced accuracy (updates every 15-20 min)
locationUnavailable
Bool
Cannot determine location
insufficientlyInUse
Bool
Can't request auth (not in foreground)
属性类型描述
location
CLLocation?
当前定位信息(不可用时为nil)
isStationary
Bool
设备是否处于静止状态
authorizationDenied
Bool
用户是否拒绝了定位权限
authorizationDeniedGlobally
Bool
系统级定位服务是否已禁用
authorizationRequestInProgress
Bool
是否正在等待用户的授权决策
accuracyLimited
Bool
是否启用了降低精度模式(每15-20分钟更新一次)
locationUnavailable
Bool
无法获取定位信息
insufficientlyInUse
Bool
无法请求权限(应用未处于前台)

Automatic Pause/Resume

自动暂停/恢复

When device becomes stationary:
  1. Final update delivered with
    isStationary = true
    and valid
    location
  2. Updates pause (saves battery)
  3. When device moves, updates resume with
    isStationary = false
No action required—happens automatically.
当设备处于静止状态时:
  1. 会发送最后一次更新,其中
    isStationary = true
    location
    有效
  2. 更新自动暂停(节省电量)
  3. 当设备开始移动时,更新恢复,
    isStationary = false
无需额外操作——该过程自动完成。

AsyncSequence Operations

AsyncSequence 操作

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

swift
// 获取第一个速度超过10m/s的定位信息
let fastUpdate = try await CLLocationUpdate.liveUpdates()
    .first { $0.location?.speed ?? 0 > 10 }

// 警告:避免使用可能永远无法匹配的过滤器(例如horizontalAccuracy < 1)

Part 3: CLMonitor API

第三部分:CLMonitor API

Swift actor for monitoring geographic conditions and beacons.
用于监控地理条件和信标的Swift actor。

Basic Geofencing

基础地理围栏

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

// 添加圆形区域
let condition = CLMonitor.CircularGeographicCondition(
    center: CLLocationCoordinate2D(latitude: 37.33, longitude: -122.01),
    radius: 100
)
await monitor.add(condition, identifier: "ApplePark")

// 监听事件
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
    }
}

CircularGeographicCondition

CircularGeographicCondition

swift
CLMonitor.CircularGeographicCondition(
    center: CLLocationCoordinate2D,
    radius: CLLocationDistance  // meters, minimum ~100m effective
)
swift
CLMonitor.CircularGeographicCondition(
    center: CLLocationCoordinate2D,
    radius: CLLocationDistance  // 单位:米,有效最小半径约100米
)

BeaconIdentityCondition

BeaconIdentityCondition

Three granularity levels:
swift
// 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)
三种粒度级别:
swift
// 监控所有具有该UUID的信标(任意站点)
CLMonitor.BeaconIdentityCondition(uuid: myUUID)

// 监控特定站点(UUID + major)
CLMonitor.BeaconIdentityCondition(uuid: myUUID, major: 100)

// 监控特定信标(UUID + major + minor)
CLMonitor.BeaconIdentityCondition(uuid: myUUID, major: 100, minor: 5)

Condition Limit

条件数量限制

Maximum 20 conditions per app. Prioritize what to monitor. Swap regions dynamically based on user location if needed.
每个应用最多支持20个条件。优先监控关键区域,必要时可根据用户位置动态替换区域。

Adding with Assumed State

假设初始状态添加条件

swift
// If you know initial state
await monitor.add(condition, identifier: "Work", assuming: .unsatisfied)
Core Location will correct if assumption wrong.
swift
// 若已知初始状态
await monitor.add(condition, identifier: "Work", assuming: .unsatisfied)
Core Location会在假设错误时自动修正状态。

Accessing Records

访问记录

swift
// 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
swift
// 获取单个记录
if let record = await monitor.record(for: "ApplePark") {
    let condition = record.condition
    let lastEvent = record.lastEvent
    let state = lastEvent.state
    let date = lastEvent.date
}

// 获取所有标识符
let allIds = await monitor.identifiers

Event Properties

事件属性

PropertyDescription
identifier
String identifier of condition
state
.satisfied
,
.unsatisfied
,
.unknown
date
When state changed
refinement
For wildcard beacons, actual UUID/major/minor detected
conditionLimitExceeded
Too many conditions (max 20)
conditionUnsupported
Condition type not available
accuracyLimited
Reduced accuracy prevents monitoring
属性描述
identifier
条件的字符串标识符
state
.satisfied
(满足)、
.unsatisfied
(不满足)、
.unknown
(未知)
date
状态变更的时间
refinement
对于通配符信标,显示检测到的实际UUID/major/minor
conditionLimitExceeded
是否超出条件数量上限(最多20个)
conditionUnsupported
条件类型是否不被支持
accuracyLimited
降低精度模式是否影响监控

Critical Requirements

关键要求

  1. One monitor per name — Only one instance with given name at a time
  2. Always await events — Events only become
    lastEvent
    after handling
  3. Reinitialize on launch — Recreate monitor in
    didFinishLaunchingWithOptions

  1. 每个名称对应一个监控实例——同一时间只能存在一个指定名称的CLMonitor实例
  2. 始终监听事件——事件只有在被处理后才会成为
    lastEvent
  3. 启动时重新初始化——在
    didFinishLaunchingWithOptions
    中重新创建监控实例

Part 4: CLServiceSession API (iOS 18+)

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

Declarative authorization—tell Core Location what you need, not what to do.
声明式授权——告知Core Location你的需求,而非具体操作。

Basic Usage

基础用法

swift
// Hold session for duration of feature
let session = CLServiceSession(authorization: .whenInUse)

for try await update in CLLocationUpdate.liveUpdates() {
    // Process updates
}
swift
// 在功能存续期间持有会话
let session = CLServiceSession(authorization: .whenInUse)

for try await update in CLLocationUpdate.liveUpdates() {
    // 处理定位更新
}

Authorization Requirements

授权要求

swift
CLServiceSession(authorization: .none)       // No auth request
CLServiceSession(authorization: .whenInUse)  // Request When In Use
CLServiceSession(authorization: .always)     // Request Always (must start in foreground)
swift
CLServiceSession(authorization: .none)       // 不请求权限
CLServiceSession(authorization: .whenInUse)  // 请求使用期间权限
CLServiceSession(authorization: .always)     // 请求始终允许权限(必须在前台启动)

Full Accuracy Request

全精度请求

swift
// For features requiring precise location (e.g., navigation)
CLServiceSession(
    authorization: .whenInUse,
    fullAccuracyPurposeKey: "NavigationPurpose"  // Key in Info.plist
)
Requires
NSLocationTemporaryUsageDescriptionDictionary
in Info.plist.
swift
// 对于需要精确定位的功能(例如导航)
CLServiceSession(
    authorization: .whenInUse,
    fullAccuracyPurposeKey: "NavigationPurpose"  // Info.plist中的键
)
需要在Info.plist中配置
NSLocationTemporaryUsageDescriptionDictionary

Implicit Sessions

隐式会话

Iterating
CLLocationUpdate.liveUpdates()
or
CLMonitor.events
creates implicit session with
.whenInUse
goal.
To disable implicit sessions:
xml
<!-- Info.plist -->
<key>NSLocationRequireExplicitServiceSession</key>
<true/>
遍历
CLLocationUpdate.liveUpdates()
CLMonitor.events
时,会自动创建一个授权目标为
.whenInUse
的隐式会话。
如需禁用隐式会话:
xml
<!-- Info.plist -->
<key>NSLocationRequireExplicitServiceSession</key>
<true/>

Session Layering

会话分层

Don't replace sessions—layer them:
swift
// 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
不要替换会话——而是分层使用:
swift
// 应用的基础会话
let baseSession = CLServiceSession(authorization: .whenInUse)

// 导航功能激活时的附加会话
let navSession = CLServiceSession(
    authorization: .whenInUse,
    fullAccuracyPurposeKey: "Nav"
)
// 两个会话同时处于活跃状态

Diagnostic Properties

诊断属性

swift
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
    }
}
swift
for try await diagnostic in session.diagnostics {
    if diagnostic.authorizationDenied {
        // 用户拒绝了权限——提供替代方案
    }
    if diagnostic.authorizationDeniedGlobally {
        // 系统级定位服务已关闭
    }
    if diagnostic.insufficientlyInUse {
        // 无法请求权限(应用未处于前台)
    }
    if diagnostic.alwaysAuthorizationDenied {
        // 始终允许权限被明确拒绝
    }
    if !diagnostic.authorizationRequestInProgress {
        // 授权决策已完成(允许或拒绝)
        break
    }
}

Session Lifecycle

会话生命周期

Sessions persist through:
  • App backgrounding
  • App suspension
  • App termination (Core Location tracks)
On relaunch, recreate sessions immediately in
didFinishLaunchingWithOptions
.

会话会在以下场景中持续存在:
  • 应用进入后台
  • 应用被挂起
  • 应用终止(Core Location会跟踪会话)
应用重启时,需立即在
didFinishLaunchingWithOptions
中重新创建会话。

Part 5: Authorization State Machine

第五部分:授权状态机

Authorization Levels

授权级别

StatusDescription
.notDetermined
User hasn't decided
.restricted
Parental controls prevent access
.denied
User explicitly refused
.authorizedWhenInUse
Access while app active
.authorizedAlways
Background access
状态描述
.notDetermined
用户尚未做出决策
.restricted
家长控制限制了定位访问
.denied
用户明确拒绝了权限
.authorizedWhenInUse
应用活跃时可访问定位
.authorizedAlways
后台也可访问定位

Accuracy Authorization

精度授权

ValueDescription
.fullAccuracy
Precise location
.reducedAccuracy
Approximate (~5km), updates every 15-20 min
描述
.fullAccuracy
精确定位
.reducedAccuracy
近似定位(约5公里),每15-20分钟更新一次

Required Info.plist Keys

必需的Info.plist键

xml
<!-- Required for When In Use -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to show nearby places</string>

<!-- Required for Always -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We track your location to send arrival reminders</string>

<!-- Optional: default to reduced accuracy -->
<key>NSLocationDefaultAccuracyReduced</key>
<true/>
xml
<!-- 使用期间权限必需 -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>我们需要获取你的位置以显示附近地点</string>

<!-- 始终允许权限必需 -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>我们会跟踪你的位置以发送到达提醒</string>

<!-- 可选:默认使用降低精度模式 -->
<key>NSLocationDefaultAccuracyReduced</key>
<true/>

Legacy Authorization Pattern

传统授权模式

swift
@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
        }
    }
}

swift
@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
        }
    }
}

Part 6: Background Location

第六部分:后台定位

Requirements

要求

  1. Background mode capability: Signing & Capabilities → Background Modes → Location updates
  2. Info.plist: Adds
    UIBackgroundModes
    with
    location
    value
  3. CLBackgroundActivitySession or LiveActivity
  1. 后台模式权限:在Signing & Capabilities中开启Background Modes → Location updates
  2. Info.plist:添加
    UIBackgroundModes
    键,值包含
    location
  3. CLBackgroundActivitySessionLiveActivity

CLBackgroundActivitySession

CLBackgroundActivitySession

swift
// 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
}
swift
// 创建并持有引用(释放会使会话失效)
var backgroundSession: CLBackgroundActivitySession?

func startBackgroundTracking() {
    // 必须从前台启动
    backgroundSession = CLBackgroundActivitySession()

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

func stopBackgroundTracking() {
    backgroundSession?.invalidate()
    backgroundSession = nil
}

Background Indicator

后台指示器

Blue status bar/pill appears when:
  • App authorized as "When In Use"
  • App receiving location in background
  • CLBackgroundActivitySession active
当以下条件满足时,会显示蓝色状态栏/标记:
  • 应用被授权为"使用期间允许"
  • 应用在后台接收定位更新
  • CLBackgroundActivitySession处于活跃状态

App Lifecycle

应用生命周期

  1. Foreground → Background: Session continues
  2. Background → Suspended: Session preserved, updates pause
  3. Suspended → Terminated: Core Location tracks session
  4. Terminated → Background launch: Recreate session immediately
swift
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Recreate background session if was tracking
    if wasTrackingLocation {
        backgroundSession = CLBackgroundActivitySession()
        startLocationUpdates()
    }
    return true
}

  1. 前台→后台:会话继续运行
  2. 后台→挂起:会话保留,更新暂停
  3. 挂起→终止:Core Location会跟踪会话
  4. 终止→后台启动:立即重新创建会话
swift
func application(_ application: UIApplication,
                 didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // 若之前在跟踪定位,重新创建后台会话
    if wasTrackingLocation {
        backgroundSession = CLBackgroundActivitySession()
        startLocationUpdates()
    }
    return true
}

Part 7: Legacy APIs (iOS 12-16)

第七部分:传统API(iOS 12-16)

CLLocationManager Delegate Pattern

CLLocationManager 代理模式

swift
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
    }
}
swift
class LocationManager: NSObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()

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

    func startUpdates() {
        manager.startUpdatingLocation()
    }

    func stopUpdates() {
        manager.stopUpdatingLocation()
    }

    func locationManager(_ manager: CLLocationManager,
                        didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        // 处理定位信息
    }
}

Accuracy Constants

精度常量

ConstantAccuracyBattery Impact
kCLLocationAccuracyBestForNavigation
~5mHighest
kCLLocationAccuracyBest
~10mVery High
kCLLocationAccuracyNearestTenMeters
~10mHigh
kCLLocationAccuracyHundredMeters
~100mMedium
kCLLocationAccuracyKilometer
~1kmLow
kCLLocationAccuracyThreeKilometers
~3kmVery Low
kCLLocationAccuracyReduced
~5kmLowest
常量精度电池影响
kCLLocationAccuracyBestForNavigation
~5米最高
kCLLocationAccuracyBest
~10米极高
kCLLocationAccuracyNearestTenMeters
~10米
kCLLocationAccuracyHundredMeters
~100米中等
kCLLocationAccuracyKilometer
~1公里
kCLLocationAccuracyThreeKilometers
~3公里极低
kCLLocationAccuracyReduced
~5公里最低

Legacy Region Monitoring

传统区域监控

swift
// 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)
swift
// iOS 17中已废弃,建议使用CLMonitor
let region = CLCircularRegion(
    center: coordinate,
    radius: 100,
    identifier: "MyRegion"
)
region.notifyOnEntry = true
region.notifyOnExit = true
manager.startMonitoring(for: region)

Significant Location Changes

显著位置变化监控

Low-power alternative for coarse tracking:
swift
manager.startMonitoringSignificantLocationChanges()
// Updates ~500m movements, works in background
低功耗的粗略定位替代方案:
swift
manager.startMonitoringSignificantLocationChanges()
// 当位置变化约500米时更新,支持后台运行

Visit Monitoring

到访监控

Detect arrivals/departures:
swift
manager.startMonitoringVisits()

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

检测到达/离开事件:
swift
manager.startMonitoringVisits()

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

Part 8: Geofencing Best Practices

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

Region Size

区域大小

  • Minimum effective radius: ~100 meters
  • Smaller regions: May not trigger reliably
  • Larger regions: More reliable but less precise
  • 有效最小半径:约100米
  • 更小的区域:可能无法可靠触发
  • 更大的区域:更可靠但精度较低

20-Region Limit Strategy

20个区域上限的应对策略

swift
// 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)
    }
}
swift
// 动态区域管理
func updateMonitoredRegions(userLocation: CLLocation) async {
    let nearbyPOIs = fetchNearbyPOIs(around: userLocation, limit: 20)

    // 移除旧区域
    for id in await monitor.identifiers {
        if !nearbyPOIs.contains(where: { $0.id == id }) {
            await monitor.remove(id)
        }
    }

    // 添加新区域
    for poi in nearbyPOIs {
        let condition = CLMonitor.CircularGeographicCondition(
            center: poi.coordinate,
            radius: 100
        )
        await monitor.add(condition, identifier: poi.id)
    }
}

Entry/Exit Timing

进入/离开触发时机

  • Entry: Usually within seconds to minutes
  • Exit: May take 3-5 minutes after leaving
  • Accuracy depends on: Cell towers, WiFi, GPS availability
  • 进入:通常在几秒到几分钟内触发
  • 离开:离开后可能需要3-5分钟才会触发
  • 精度取决于:基站、WiFi、GPS的可用性

Persistence

持久性

  • Conditions persist across app launches
  • Must reinitialize monitor with same name on launch
  • Core Location wakes app for events

  • 条件会在应用重启后保留
  • 启动时必须使用相同名称重新初始化监控实例
  • Core Location会在事件触发时唤醒应用

Part 9: Testing and Simulation

第九部分:测试与模拟

Xcode Location Simulation

Xcode定位模拟

  1. Run on simulator
  2. Debug → Simulate Location → Choose location
  3. Or use custom GPX file
  1. 在模拟器上运行应用
  2. 点击Debug → Simulate Location → 选择位置
  3. 或使用自定义GPX文件

Custom GPX Route

自定义GPX路线

xml
<?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>
xml
<?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>

Testing Authorization States

测试授权状态

Settings → Privacy & Security → Location Services:
  • Toggle app authorization
  • Toggle system-wide location services
  • Test reduced accuracy
进入设置→隐私与安全性→定位服务:
  • 切换应用的授权状态
  • 切换系统级定位服务开关
  • 测试降低精度模式

Console Filtering

控制台过滤

bash
undefined
bash
undefined

Filter location logs

过滤定位相关日志

log stream --predicate 'subsystem == "com.apple.locationd"'

---
log stream --predicate 'subsystem == "com.apple.locationd"'

---

Part 10: Swift Concurrency Integration

第十部分:Swift并发集成

Task Cancellation

任务取消

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

// Later
locationTask.cancel()
swift
let locationTask = Task {
    for try await update in CLLocationUpdate.liveUpdates() {
        if Task.isCancelled { break }
        processUpdate(update)
    }
}

// 后续取消任务
locationTask.cancel()

MainActor Considerations

MainActor 注意事项

swift
@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
            }
        }
    }
}
swift
@MainActor
class LocationViewModel: ObservableObject {
    @Published var currentLocation: CLLocation?

    func startTracking() {
        Task {
            for try await update in CLLocationUpdate.liveUpdates() {
                // 已在MainActor中,可安全更新@Published属性
                self.currentLocation = update.location
            }
        }
    }
}

Error Handling

错误处理

swift
Task {
    do {
        for try await update in CLLocationUpdate.liveUpdates() {
            if update.authorizationDenied {
                throw LocationError.authorizationDenied
            }
            processUpdate(update)
        }
    } catch {
        handleError(error)
    }
}

swift
Task {
    do {
        for try await update in CLLocationUpdate.liveUpdates() {
            if update.authorizationDenied {
                throw LocationError.authorizationDenied
            }
            processUpdate(update)
        }
    } catch {
        handleError(error)
    }
}

Troubleshooting Quick Reference

故障排除快速参考

SymptomCheck
No location updatesAuthorization status, Info.plist keys
Background not workingBackground mode capability, CLBackgroundActivitySession
Always auth not effectiveCLServiceSession with
.always
, started in foreground
Geofence not triggeringRegion count (max 20), radius (min ~100m)
Reduced accuracy onlyCheck
accuracyAuthorization
, request temporary full accuracy
Location icon stays onEnsure
stopUpdatingLocation()
or break from async loop

症状检查项
无定位更新授权状态、Info.plist键配置
后台定位不工作后台模式权限、CLBackgroundActivitySession
始终允许权限无效使用
.always
授权的CLServiceSession,且在前台启动
地理围栏未触发区域数量(最多20个)、半径(最小约100米)
仅能获取降低精度的定位检查
accuracyAuthorization
,请求临时全精度权限
定位图标持续显示确保调用
stopUpdatingLocation()
或退出异步循环

Resources

资源

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