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 以降の新しい API と従来の実装パターンを取り上げ、位置情報更新、ジオフェンス、認証、バックグラウンド設定を説明します。

主な機能

  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(iOS 17 以降)の包括的な API リファレンスです。

使用する場面

  • CLLocationUpdate、CLMonitor、CLServiceSession の API シグネチャが必要な場合
  • ジオフェンシングや領域モニタリングを実装する場合
  • バックグラウンドでの位置情報更新を設定する場合
  • 認証パターンを理解する場合
  • 位置情報サービスの問題をデバッグする場合

関連するスキル

  • axiom-core-location — アンチパターン、判断ツリー、負荷のかかるシナリオ
  • axiom-core-location-diag — 症状に基づくトラブルシューティング
  • axiom-energy-ref — バッテリーサブシステムとしての位置情報(精度と電力の比較)

Part 1: 最新 API の概要(iOS 17 以降)

4 つの主要なクラスが、従来の CLLocationManager パターンに代わります。

クラス目的iOS
CLLocationUpdate位置情報更新の AsyncSequence17+
CLMonitor条件ベースのジオフェンシング/ビーコン17+
CLServiceSession宣言型の認証目標18+
CLBackgroundActivitySessionバックグラウンドでの位置情報サポート17+

移行パス:従来の CLLocationManager も引き続き動作しますが、新しい API では次の機能が利用できます。

  • Swift の並行処理(async/await)
  • 自動的な一時停止/再開
  • 簡素化された認証
  • バッテリー効率の向上

Part 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)

Part 3: CLMonitor API

地理的な条件やビーコンをモニタリングするための 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
    }
}
CircularGeographicCondition
CLMonitor.CircularGeographicCondition(
    center: CLLocationCoordinate2D,
    radius: CLLocationDistance  // meters, minimum ~100m effective
)
BeaconIdentityCondition

粒度は次の 3 段階です。

// 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. 名前ごとにモニターは 1 つ — 同じ名前のインスタンスは、同時に 1 つのみ使用できます
  2. イベントは必ず await する — イベントの処理後にのみ lastEvent に反映されます
  3. 起動時に再初期化する — didFinishLaunchingWithOptions でモニターを再作成します

Part 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
    }
}
セッションのライフサイクル

セッションは次の状況でも維持されます:

  • アプリがバックグラウンドに移行したとき
  • アプリが一時停止したとき
  • アプリが終了したとき(Core Location が追跡します)

再起動時には、didFinishLaunchingWithOptions で直ちにセッションを再作成します。


パート 5:認証状態の状態機械

認証レベル
ステータス説明
.notDeterminedユーザーはまだ決定していません
.restrictedペアレンタルコントロールによりアクセスが制限されています
.deniedユーザーが明示的に拒否しました
.authorizedWhenInUseアプリがアクティブな間のアクセス
.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:UIBackgroundModes に location の値を追加します
  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
}
バックグラウンドインジケーター

次の場合に、青色のステータスバー/ピルが表示されます:

  • アプリの認証が「使用中のみ」である
  • アプリがバックグラウンドで位置情報を受信している
  • CLBackgroundActivitySession がアクティブである
アプリのライフサイクル
  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 の利用可否
永続性
  • 条件はアプリの起動をまたいで維持されます
  • 起動時に同じ名前でモニターを再初期化する必要があります
  • Core Location はイベント発生時にアプリを起動します

パート 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>
認証状態のテスト

設定 → プライバシーとセキュリティ → 位置情報サービス:

  • アプリの認証を切り替えます
  • システム全体の位置情報サービスを切り替えます
  • 精度を下げた状態をテストします
コンソールのフィルタリング
# Filter location logs
log stream --predicate 'subsystem == "com.apple.locationd"'

パート 10:Swift Concurrency との統合

タスクのキャンセル
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座標
ジオコーディングのレート制限
  • 一度にリクエストは 1 件まで — リクエストの処理中に CLGeocoder を呼び出すとエラーが発生します
  • Apple によるレート制限 — kCLErrorGeocodeCanceled を避けるため、リクエストの頻度を抑えてください
  • 結果をキャッシュする — 同じ住所や座標を再度ジオコーディングしないでください
  • バッチ処理は慎重に — 連続するジオコーディングリクエストの間に遅延を設けてください
// Check if geocoder is busy
if geocoder.isGeocoding {
    geocoder.cancelGeocode()  // Cancel previous before starting new
}

トラブルシューティング早見表

症状確認項目
位置情報が更新されない認証ステータス、Info.plist のキー
バックグラウンドで動作しないバックグラウンドモードの機能、CLBackgroundActivitySession
「常に許可」が有効にならない.always を指定した CLServiceSession、フォアグラウンドで開始しているか
ジオフェンスが作動しないリージョン数(最大 20)、半径(最小約 100 m)
精度が低い状態のまま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

コメント