Claude Code Plugins

Community-maintained marketplace

Feedback

axiom-core-location

@CharlesWiltgen/Axiom
157
0

Use for Core Location implementation patterns - authorization strategy, monitoring strategy, accuracy selection, background location

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name axiom-core-location
description Use for Core Location implementation patterns - authorization strategy, monitoring strategy, accuracy selection, background location
skill_type discipline
version 1.0.0
apple_platforms iOS 17+, iPadOS 17+, macOS 14+, watchOS 10+
last_updated Sat Jan 03 2026 00:00:00 GMT+0000 (Coordinated Universal Time)

Core Location Patterns

Discipline skill for Core Location implementation decisions. Prevents common authorization mistakes, battery drain, and background location failures.

When to Use

  • Choosing authorization strategy (When In Use vs Always)
  • Deciding monitoring approach (continuous vs significant-change vs CLMonitor)
  • Implementing geofencing or background location
  • Debugging "location not working" issues
  • Reviewing location code for anti-patterns

Related Skills

  • axiom-core-location-ref — API reference, code examples
  • axiom-core-location-diag — Symptom-based troubleshooting
  • axiom-energy — Location as battery subsystem

Part 1: Anti-Patterns (with Time Costs)

Anti-Pattern 1: Premature Always Authorization

Wrong (30-60% denial rate):

// First launch: "Can we have Always access?"
manager.requestAlwaysAuthorization()

Right (5-10% denial rate):

// Start with When In Use
CLServiceSession(authorization: .whenInUse)

// Later, when user triggers background feature:
CLServiceSession(authorization: .always)

Time cost: 15 min to fix code, but 30-60% of users permanently denied = feature adoption destroyed.

Why: Users deny aggressive requests. Start minimal, upgrade when user understands value.


Anti-Pattern 2: Continuous Updates for Geofencing

Wrong (10x battery drain):

for try await update in CLLocationUpdate.liveUpdates() {
    if isNearTarget(update.location) {
        triggerGeofence()
    }
}

Right (system-managed, low power):

let monitor = await CLMonitor("Geofences")
let condition = CLMonitor.CircularGeographicCondition(
    center: target, radius: 100
)
await monitor.add(condition, identifier: "Target")

for try await event in monitor.events {
    if event.state == .satisfied { triggerGeofence() }
}

Time cost: 5 min to refactor, saves 10x battery.


Anti-Pattern 3: Ignoring Stationary Detection

Wrong (wasted battery):

for try await update in CLLocationUpdate.liveUpdates() {
    processLocation(update.location)
    // Never stops, even when device stationary
}

Right (automatic pause/resume):

for try await update in CLLocationUpdate.liveUpdates() {
    if let location = update.location {
        processLocation(location)
    }
    if update.isStationary, let location = update.location {
        // Device stopped moving - updates pause automatically
        // Will resume when device moves again
        saveLastKnownLocation(location)
    }
}

Time cost: 2 min to add check, saves significant battery.


Anti-Pattern 4: No Graceful Denial Handling

Wrong (broken UX):

for try await update in CLLocationUpdate.liveUpdates() {
    guard let location = update.location else { continue }
    // User denied - silent failure, no feedback
}

Right (graceful degradation):

for try await update in CLLocationUpdate.liveUpdates() {
    if update.authorizationDenied {
        showManualLocationPicker()
        break
    }
    if update.authorizationDeniedGlobally {
        showSystemLocationDisabledMessage()
        break
    }
    if let location = update.location {
        processLocation(location)
    }
}

Time cost: 10 min to add handling, prevents confused users.


Anti-Pattern 5: Wrong Accuracy for Use Case

Wrong (battery drain for weather app):

// Weather app using navigation accuracy
CLLocationUpdate.liveUpdates(.automotiveNavigation)

Right (match accuracy to need):

// Weather: city-level is fine
CLLocationUpdate.liveUpdates(.default)  // or .fitness for runners

// Navigation: needs high accuracy
CLLocationUpdate.liveUpdates(.automotiveNavigation)
Use Case Configuration Accuracy Battery
Navigation .automotiveNavigation ~5m Highest
Fitness tracking .fitness ~10m High
Store finder .default ~10-100m Medium
Weather .default ~100m+ Low

Time cost: 1 min to change, significant battery savings.


Anti-Pattern 6: Not Stopping Updates

Wrong (battery drain, location icon persists):

func viewDidLoad() {
    Task {
        for try await update in CLLocationUpdate.liveUpdates() {
            updateMap(update.location)
        }
    }
}
// User navigates away, updates continue forever

Right (cancel when done):

private var locationTask: Task<Void, Error>?

func startTracking() {
    locationTask = Task {
        for try await update in CLLocationUpdate.liveUpdates() {
            if Task.isCancelled { break }
            updateMap(update.location)
        }
    }
}

func stopTracking() {
    locationTask?.cancel()
    locationTask = nil
}

Time cost: 5 min to add cancellation, stops battery drain.


Anti-Pattern 7: Ignoring CLServiceSession (iOS 18+)

Wrong (procedural authorization juggling):

func requestAuth() {
    switch manager.authorizationStatus {
    case .notDetermined:
        manager.requestWhenInUseAuthorization()
    case .authorizedWhenInUse:
        if needsFullAccuracy {
            manager.requestTemporaryFullAccuracyAuthorization(...)
        }
    // Complex state machine...
    }
}

Right (declarative goals):

// Just declare what you need - Core Location handles the rest
let session = CLServiceSession(authorization: .whenInUse)

// For feature needing full accuracy
let navSession = CLServiceSession(
    authorization: .whenInUse,
    fullAccuracyPurposeKey: "Navigation"
)

// Monitor diagnostics if needed
for try await diag in session.diagnostics {
    if diag.authorizationDenied { handleDenial() }
}

Time cost: 30 min to migrate, simpler code, fewer bugs.


Part 2: Decision Trees

Authorization Strategy

Q1: Does your feature REQUIRE background location?
├─ NO → Use .whenInUse
│   └─ Q2: Does any feature need precise location?
│       ├─ ALWAYS → Add fullAccuracyPurposeKey to session
│       └─ SOMETIMES → Layer full-accuracy session when feature active
│
└─ YES → Start with .whenInUse, upgrade to .always when user triggers feature
    └─ Q3: When does user first need background location?
        ├─ IMMEDIATELY (e.g., fitness tracker) → Request .always on first relevant action
        └─ LATER (e.g., geofence reminders) → Add .always session when user creates first geofence

Monitoring Strategy

Q1: What are you monitoring for?
├─ USER POSITION (continuous tracking)
│   └─ Use CLLocationUpdate.liveUpdates()
│       └─ Q2: What activity?
│           ├─ Driving navigation → .automotiveNavigation
│           ├─ Walking/cycling nav → .otherNavigation
│           ├─ Fitness tracking → .fitness
│           ├─ Airplane apps → .airborne
│           └─ General → .default or omit
│
├─ ENTRY/EXIT REGIONS (geofencing)
│   └─ Use CLMonitor with CircularGeographicCondition
│       └─ Note: Maximum 20 conditions per app
│
├─ BEACON PROXIMITY
│   └─ Use CLMonitor with BeaconIdentityCondition
│       └─ Choose granularity: UUID only, UUID+major, UUID+major+minor
│
└─ SIGNIFICANT CHANGES ONLY (lowest power)
    └─ Use startMonitoringSignificantLocationChanges() (legacy)
        └─ Updates ~500m movements, works in background

Accuracy Selection

Q1: What's the minimum accuracy that makes your feature work?
├─ TURN-BY-TURN NAV needs 5-10m → .automotiveNavigation / .otherNavigation
├─ FITNESS TRACKING needs 10-20m → .fitness
├─ STORE FINDER needs 100m → .default
├─ WEATHER/CITY needs 1km+ → .default (reduced accuracy acceptable)
└─ GEOFENCING uses system determination → CLMonitor handles it

Q2: Will user be moving fast?
├─ DRIVING (high speed) → .automotiveNavigation (extra processing for speed)
├─ CYCLING/WALKING → .otherNavigation
└─ STATIONARY/SLOW → .default

Always start with lowest acceptable accuracy. Higher accuracy = higher battery drain.

Part 3: Pressure Scenarios

Scenario 1: "Just Use Always Authorization"

Context: PM says "Users want location reminders. Just request Always access on first launch so it works."

Pressure: Ship fast, seems simpler.

Reality:

  • 30-60% of users will deny Always authorization when asked upfront
  • Users who deny can only re-enable in Settings (most won't)
  • Feature adoption destroyed before users understand value

Response:

"Always authorization has 30-60% denial rates when requested upfront. We should start with When In Use, then request Always upgrade when the user creates their first location reminder. This gives us a 5-10% denial rate because users understand why they need it."

Evidence: Apple's own guidance in WWDC 2024-10212: "CLServiceSessions should be taken proactively... hold one requiring full-accuracy when people engage a feature that would warrant a special ask for it."


Scenario 2: "Location Isn't Working in Background"

Context: QA reports "App stops getting location when backgrounded."

Pressure: Quick fix before release.

Wrong fixes:

  • Add all background modes
  • Use allowsBackgroundLocationUpdates = true without understanding
  • Request Always authorization

Right diagnosis:

  1. Check background mode capability exists
  2. Check CLBackgroundActivitySession is held (not deallocated)
  3. Check session started from foreground
  4. Check authorization level (.whenInUse works with CLBackgroundActivitySession)

Response:

"Background location requires specific setup. Let me check: (1) Background mode capability, (2) CLBackgroundActivitySession held during tracking, (3) session started from foreground. Missing any of these causes silent failure."

Checklist:

// 1. Signing & Capabilities → Background Modes → Location updates
// 2. Hold session reference (property, not local variable)
var backgroundSession: CLBackgroundActivitySession?

func startBackgroundTracking() {
    // 3. Must start from foreground
    backgroundSession = CLBackgroundActivitySession()
    startLocationUpdates()
}

Scenario 3: "Geofence Events Aren't Firing"

Context: Geofences work in testing but not in production for some users.

Pressure: "It works on my device" dismissal.

Common causes:

  1. Too many conditions: Maximum 20 per app
  2. Radius too small: Minimum ~100m for reliable triggering
  3. Overlapping regions: Can cause confusion
  4. Not awaiting events: Events only become lastEvent after handled
  5. Not reinitializing on launch: Monitor must be recreated

Response:

"Geofencing has several system constraints. Check: (1) Are we within the 20-condition limit? (2) Are all radii at least 100m? (3) Is the app reinitializing CLMonitor on launch? (4) Is the app always awaiting on monitor.events?"

Diagnostic code:

// Check condition count
let count = await monitor.identifiers.count
if count >= 20 {
    print("At 20-condition limit!")
}

// Check all conditions
for id in await monitor.identifiers {
    if let record = await monitor.record(for: id) {
        let condition = record.condition
        if let geo = condition as? CLMonitor.CircularGeographicCondition {
            if geo.radius < 100 {
                print("Radius too small: \(id)")
            }
        }
    }
}

Part 4: Checklists

Pre-Release Location Checklist

Info.plist:

  • NSLocationWhenInUseUsageDescription with clear explanation
  • NSLocationAlwaysAndWhenInUseUsageDescription if using Always (clear why background needed)
  • NSLocationDefaultAccuracyReduced if reduced accuracy acceptable
  • NSLocationTemporaryUsageDescriptionDictionary if requesting temporary full accuracy
  • UIBackgroundModes includes location if background tracking

Authorization:

  • Start with minimal authorization (.whenInUse)
  • Upgrade to .always only when user triggers background feature
  • Handle authorization denial gracefully (offer alternatives)
  • Handle global location services disabled
  • Test with reduced accuracy authorization

Updates:

  • Using appropriate LiveConfiguration for use case
  • Handling isStationary for pause/resume
  • Cancelling location tasks when feature inactive
  • Not using continuous updates for geofencing

Testing:

  • Tested authorization denial flow
  • Tested reduced accuracy mode
  • Tested background-to-foreground transitions
  • Tested app termination and relaunch recovery

Background Location Checklist

Setup:

  • Background mode capability added (Location updates)
  • CLBackgroundActivitySession created and HELD (not local variable)
  • Session started from foreground
  • Updates restarted on background launch in didFinishLaunchingWithOptions

Authorization:

  • Using .whenInUse with CLBackgroundActivitySession, OR
  • Using .always (but only if needed beyond background indicator)

Lifecycle:

  • Persisting "was tracking" state for relaunch recovery
  • Recreating CLBackgroundActivitySession on background launch
  • Restarting CLLocationUpdate iteration on launch
  • CLMonitor reinitialized with same name on launch

Testing:

  • Blue background location indicator appears when backgrounded
  • Updates continue when app backgrounded
  • Updates resume after app suspended and resumed
  • Updates resume after app terminated and relaunched

Part 5: iOS Version Considerations

Feature iOS Version Notes
CLLocationUpdate iOS 17+ AsyncSequence API
CLMonitor iOS 17+ Replaces CLCircularRegion
CLBackgroundActivitySession iOS 17+ Background with blue indicator
CLServiceSession iOS 18+ Declarative authorization
Implicit service sessions iOS 18+ From iterating liveUpdates
CLLocationManager iOS 2+ Legacy but still works

For iOS 14-16 support: Use CLLocationManager delegate pattern (see core-location-ref Part 7).

For iOS 17+: Prefer CLLocationUpdate and CLMonitor.

For iOS 18+: Add CLServiceSession for declarative authorization.


Resources

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

Docs: /corelocation, /corelocation/clmonitor, /corelocation/cllocationupdate, /corelocation/clservicesession

Skills: axiom-core-location-ref, axiom-core-location-diag, axiom-energy