Claude Code Plugins

Community-maintained marketplace

Feedback
157
0

Use when app drains battery, device gets hot, users report energy issues, or auditing power consumption - systematic Power Profiler diagnosis, subsystem identification (CPU/GPU/Network/Location/Display), anti-pattern fixes for iOS/iPadOS

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-energy
description Use when app drains battery, device gets hot, users report energy issues, or auditing power consumption - systematic Power Profiler diagnosis, subsystem identification (CPU/GPU/Network/Location/Display), anti-pattern fixes for iOS/iPadOS
skill_type discipline
version 1.0.0
mcp [object Object]

Energy Optimization

Overview

Energy issues manifest as battery drain, hot devices, and poor App Store reviews. Core principle: Measure before optimizing. Use Power Profiler to identify the dominant subsystem (CPU/GPU/Network/Location/Display), then apply targeted fixes.

Key insight: Developers often don't know where to START auditing. This skill provides systematic diagnosis, not guesswork.

Requirements: iOS 26+, Xcode 26+, Power Profiler in Instruments

Example Prompts

Real questions developers ask that this skill answers:

1. "My app is always at the top of Battery Settings. How do I find what's draining power?"

→ The skill covers Power Profiler workflow to identify dominant subsystem and targeted fixes

2. "Users report my app makes their phone hot. Where do I start debugging?"

→ The skill provides decision tree: CPU vs GPU vs Network diagnosis with specific patterns

3. "I have timers and location updates. Are they causing battery drain?"

→ The skill covers timer tolerance, location accuracy trade-offs, and audit checklists

4. "My app drains battery in the background even when users aren't using it."

→ The skill covers background execution patterns, BGTasks, and EMRCA principles

5. "How do I measure if my optimization actually improved battery life?"

→ The skill demonstrates before/after Power Profiler comparison workflow


Red Flags — High Energy Likely

If you see ANY of these, suspect energy inefficiency:

  • Battery Settings: Your app consistently at top of battery consumers
  • Device temperature: Phone gets warm during normal app use
  • User reviews: Mentions of "battery drain", "hot phone", "kills my battery"
  • Xcode Energy Gauge: Shows sustained high or very high impact
  • Background runtime: App runs longer than expected when not visible
  • Network activity: Frequent small requests instead of batched operations
  • Location icon: Appears in status bar when app shouldn't need location

Difference from normal energy use

  • Normal: App uses energy during active use, minimal when backgrounded
  • Problem: App uses significant energy even when user isn't interacting

Mandatory First Steps

ALWAYS run Power Profiler FIRST before optimizing code:

Step 1: Record a Power Trace (5 minutes)

1. Connect iPhone wirelessly to Xcode (wireless debugging)
2. Xcode → Product → Profile (Cmd+I)
3. Select Blank template
4. Click "+" → Add "Power Profiler" instrument
5. Optional: Add "CPU Profiler" for correlation
6. Click Record
7. Use your app normally for 2-3 minutes
8. Click Stop

Why wireless: When device is charging via cable, power metrics show 0. Use wireless debugging for accurate readings.

Step 2: Identify Dominant Subsystem

Expand the Power Profiler track and examine per-app metrics:

Lane Meaning High Value Indicates
CPU Power Impact Processor activity Computation, timers, parsing
GPU Power Impact Graphics rendering Animations, blur, Metal
Display Power Impact Screen usage Brightness, always-on content
Network Power Impact Radio activity Requests, downloads, polling

Look for: Which subsystem shows highest sustained values during your app's usage.

Step 3: Branch to Subsystem-Specific Fixes

Once you identify the dominant subsystem, use the decision trees below.

What this tells you

  • CPU dominant → Check timers, polling, JSON parsing, eager loading
  • GPU dominant → Check animations, blur effects, frame rates
  • Network dominant → Check request frequency, polling vs push
  • Display dominant → Check Dark Mode, brightness, screen-on time
  • Location (shown in CPU) → Check accuracy, update frequency

Why diagnostics first

  • Finding root cause with Power Profiler: 15-20 minutes
  • Guessing and testing random optimizations: 4+ hours, often wrong subsystem

Energy Decision Tree

User reports energy issue?
│
├─ CPU Power Impact dominant?
│  ├─ Continuous high impact?
│  │  ├─ Timers running? → Pattern 1: Timer Efficiency
│  │  ├─ Polling data? → Pattern 2: Push vs Poll
│  │  └─ Processing in loop? → Pattern 3: Lazy Loading
│  ├─ Spikes during specific actions?
│  │  ├─ JSON parsing? → Cache parsed results
│  │  ├─ Image processing? → Move to background, cache
│  │  └─ Database queries? → Index, batch, prefetch
│  └─ High background CPU?
│     ├─ Location updates? → Pattern 4: Location Efficiency
│     ├─ BGTasks running too long? → Pattern 5: Background Execution
│     └─ Audio session active? → Stop when not playing
│
├─ Network Power Impact dominant?
│  ├─ Many small requests?
│  │  └─ Batch into fewer large requests
│  ├─ Polling pattern detected?
│  │  └─ Convert to push notifications → Pattern 2
│  ├─ Downloads in foreground?
│  │  └─ Use discretionary background URLSession
│  └─ High cellular usage?
│     └─ Defer to WiFi when possible
│
├─ GPU Power Impact dominant?
│  ├─ Continuous animations?
│  │  └─ Stop when view not visible
│  ├─ Blur effects (UIVisualEffectView)?
│  │  └─ Reduce or remove, use solid colors
│  ├─ High frame rate animations?
│  │  └─ Audit secondary frame rates → Pattern 6
│  └─ Metal rendering?
│     └─ Implement frame limiting
│
├─ Display Power Impact dominant?
│  ├─ Light backgrounds on OLED?
│  │  └─ Implement Dark Mode (up to 70% savings)
│  ├─ High brightness content?
│  │  └─ Use darker UI elements
│  └─ Screen always on?
│     └─ Allow screen to sleep when appropriate
│
└─ Location causing drain? (check CPU lane + location icon)
   ├─ Continuous updates?
   │  └─ Switch to significant-change monitoring
   ├─ High accuracy (kCLLocationAccuracyBest)?
   │  └─ Reduce to kCLLocationAccuracyHundredMeters
   └─ Background location?
      └─ Evaluate if truly needed → Pattern 4

Common Energy Patterns (With Fixes)

Pattern 1: Timer Efficiency

Problem: Timers wake the CPU from idle states, consuming significant energy.

❌ Anti-Pattern — Timer without tolerance

// BAD: Timer fires exactly every 1.0 seconds
// Prevents system from batching with other timers
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
    self.updateUI()
}

✅ Fix — Set tolerance for timer batching

// GOOD: 10% tolerance allows system to batch timers
let timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
    self.updateUI()
}
timer.tolerance = 0.1  // 10% tolerance minimum

// BETTER: Use Combine Timer with tolerance
Timer.publish(every: 1.0, tolerance: 0.1, on: .main, in: .default)
    .autoconnect()
    .sink { [weak self] _ in
        self?.updateUI()
    }
    .store(in: &cancellables)

✅ Best — Use event-driven instead of polling

// BEST: Don't use timer at all — react to events
NotificationCenter.default.publisher(for: .dataDidUpdate)
    .sink { [weak self] _ in
        self?.updateUI()
    }
    .store(in: &cancellables)

Key points:

  • Set tolerance to at least 10% of interval
  • Timer tolerance allows system to batch multiple timers into single wake
  • Prefer event-driven patterns over polling timers
  • Always invalidate timers when no longer needed

Pattern 2: Push vs Poll

Problem: Polling (checking server every N seconds) keeps radios active and drains battery.

❌ Anti-Pattern — Polling every 5 seconds

// BAD: Polls server every 5 seconds
// Radio stays active, massive battery drain
Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in
    self?.fetchLatestData()  // Network request every 5 seconds
}

✅ Fix — Use background push notifications

// GOOD: Server pushes when data changes
// Radio only active when there's actual new data

// 1. Register for remote notifications
UIApplication.shared.registerForRemoteNotifications()

// 2. Handle background notification
func application(_ application: UIApplication,
                 didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

    guard let _ = userInfo["content-available"] else {
        completionHandler(.noData)
        return
    }

    Task {
        do {
            let hasNewData = try await fetchLatestData()
            completionHandler(hasNewData ? .newData : .noData)
        } catch {
            completionHandler(.failed)
        }
    }
}

Server payload for background push:

{
    "aps": {
        "content-available": 1
    },
    "custom-data": "your-payload"
}

Key points:

  • Background pushes are discretionary — system delivers at optimal time
  • Use apns-priority: 5 for non-urgent updates (energy efficient)
  • Use apns-priority: 10 only for time-sensitive alerts
  • Polling every 5 seconds uses 100x more energy than push

Pattern 3: Lazy Loading & Caching

Problem: Loading all data upfront causes CPU spikes and memory pressure.

❌ Anti-Pattern — Eager loading (from WWDC25-226)

// BAD: Creates and renders ALL views upfront
// From WWDC25-226: This caused CPU spike and hang
VStack {
    ForEach(videos) { video in
        VideoCardView(video: video)  // Creates ALL thumbnails immediately
    }
}

✅ Fix — Lazy loading

// GOOD: Only creates visible views
// From WWDC25-226: Reduced CPU power impact from 21 to 4.3
LazyVStack {
    ForEach(videos) { video in
        VideoCardView(video: video)  // Creates on-demand
    }
}

❌ Anti-Pattern — Repeated parsing (from WWDC25-226)

// BAD: Parses JSON file on every location update
// From WWDC25-226: Caused continuous CPU drain during commute
func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
    // Called every location change!
    let data = try? Data(contentsOf: rulesFileURL)
    let rules = try? JSONDecoder().decode([RecommendationRule].self, from: data)
    return filteredVideos(using: rules)
}

✅ Fix — Cache parsed data

// GOOD: Parse once, reuse cached result
// From WWDC25-226: Eliminated CPU drain
private lazy var cachedRules: [RecommendationRule] = {
    let data = try? Data(contentsOf: rulesFileURL)
    return (try? JSONDecoder().decode([RecommendationRule].self, from: data)) ?? []
}()

func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
    return filteredVideos(using: cachedRules)  // No parsing!
}

Key points:

  • Use LazyVStack, LazyHStack, LazyVGrid for large collections
  • Cache parsed JSON, decoded data, computed results
  • Move expensive operations out of frequently-called methods

Pattern 4: Location Efficiency

Problem: Continuous location updates keep GPS active, draining battery rapidly.

❌ Anti-Pattern — Continuous high-accuracy updates

// BAD: Continuous updates with best accuracy
// GPS stays active constantly, massive battery drain
let locationManager = CLLocationManager()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()  // Never stops!

✅ Fix — Appropriate accuracy and significant-change

// GOOD: Reduced accuracy, significant-change monitoring
let locationManager = CLLocationManager()

// Use appropriate accuracy (100m is fine for most apps)
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters

// Use distance filter to reduce updates
locationManager.distanceFilter = 100  // Only update every 100 meters

// For background: Use significant-change monitoring
locationManager.startMonitoringSignificantLocationChanges()

// Stop when done
func stopTracking() {
    locationManager.stopUpdatingLocation()
    locationManager.stopMonitoringSignificantLocationChanges()
}

✅ Better — iOS 26+ CLLocationUpdate with stationary detection

// BEST: Modern async API with automatic stationary detection
for try await update in CLLocationUpdate.liveUpdates() {
    if update.stationary {
        // Device stopped moving — system pauses updates automatically
        // Switch to CLMonitor for region monitoring
        break
    }
    handleLocation(update.location)
}

Accuracy comparison (battery impact):

Accuracy Battery Impact Use Case
kCLLocationAccuracyBest Very High Navigation apps only
kCLLocationAccuracyNearestTenMeters High Fitness tracking
kCLLocationAccuracyHundredMeters Medium Store locators
kCLLocationAccuracyKilometer Low Weather apps
Significant-change Very Low Background updates

Pattern 5: Background Execution (EMRCA)

Problem: Background tasks that run too long or too often drain battery.

EMRCA Principles (from WWDC25-227)

Your background work must be:

  • Efficient — Design lightweight, purpose-driven tasks
  • Minimal — Keep background work to a minimum
  • Resilient — Save incremental progress; respond to expiration signals
  • Courteous — Honor user preferences and system conditions
  • Adaptive — Understand and adapt to system priorities

❌ Anti-Pattern — Long-running background task

// BAD: Requests unlimited background time
// System will terminate after ~30 seconds anyway
var backgroundTask: UIBackgroundTaskIdentifier = .invalid

func applicationDidEnterBackground(_ application: UIApplication) {
    backgroundTask = application.beginBackgroundTask {
        // Expiration handler — but task runs too long
    }

    // Long operation that may not complete
    performLongOperation()
}

✅ Fix — Proper background task handling

// GOOD: Finish quickly, save progress, notify system
var backgroundTask: UIBackgroundTaskIdentifier = .invalid

func applicationDidEnterBackground(_ application: UIApplication) {
    backgroundTask = application.beginBackgroundTask(withName: "Save State") { [weak self] in
        // Expiration handler — clean up immediately
        self?.saveProgress()
        if let task = self?.backgroundTask {
            application.endBackgroundTask(task)
        }
        self?.backgroundTask = .invalid
    }

    // Quick operation
    saveEssentialState()

    // End task as soon as done — don't wait for expiration
    application.endBackgroundTask(backgroundTask)
    backgroundTask = .invalid
}

✅ For Long Operations — Use BGProcessingTask

// BEST: Let system schedule at optimal time (charging, WiFi)
func scheduleBackgroundProcessing() {
    let request = BGProcessingTaskRequest(identifier: "com.app.maintenance")
    request.requiresNetworkConnectivity = true
    request.requiresExternalPower = true  // Only when charging

    try? BGTaskScheduler.shared.submit(request)
}

// Register handler at app launch
BGTaskScheduler.shared.register(
    forTaskWithIdentifier: "com.app.maintenance",
    using: nil
) { task in
    self.handleMaintenance(task: task as! BGProcessingTask)
}

✅ iOS 26+ — BGContinuedProcessingTask for user-initiated work

// NEW iOS 26: Continue user-initiated tasks with progress UI
let request = BGContinuedProcessingTaskRequest(
    identifier: "com.app.export",
    title: "Exporting Photos",
    subtitle: "23 of 100 photos"
)

try? BGTaskScheduler.shared.submit(request)

Pattern 6: Frame Rate Auditing

Problem: Secondary animations running at higher frame rates than needed increase GPU power.

❌ Anti-Pattern — Uncontrolled frame rates

// BAD: Secondary animation runs at 60fps
// When primary content only needs 30fps, this wastes power
UIView.animate(withDuration: 2.0, delay: 0, options: [.repeat]) {
    self.subtitleLabel.alpha = 0.5
} completion: { _ in
    self.subtitleLabel.alpha = 1.0
}

✅ Fix — Control frame rate with CADisplayLink

// GOOD: Explicitly set preferred frame rate
let displayLink = CADisplayLink(target: self, selector: #selector(updateAnimation))
displayLink.preferredFrameRateRange = CAFrameRateRange(
    minimum: 10,
    maximum: 30,  // Match primary content
    preferred: 30
)
displayLink.add(to: .current, forMode: .default)

From WWDC22-10083: Up to 20% battery savings by aligning secondary animation frame rates with primary content.


Audit Checklists

Timer Audit

  • All timers have tolerance set (≥10% of interval)?
  • Timers invalidated when no longer needed?
  • Using Combine Timer instead of NSTimer where possible?
  • No polling patterns that could use push notifications?
  • Timers stopped when app enters background?

Network Audit

  • Requests batched instead of many small requests?
  • Using discretionary URLSession for non-urgent downloads?
  • waitsForConnectivity set to avoid failed connection attempts?
  • allowsExpensiveNetworkAccess set to false for deferrable work?
  • Push notifications instead of polling?

Location Audit

  • Using appropriate accuracy (not kCLLocationAccuracyBest unless navigation)?
  • distanceFilter set to reduce update frequency?
  • Stopping updates when no longer needed?
  • Using significant-change for background updates?
  • Background location justified and explained to users?

Background Execution Audit

  • endBackgroundTask called promptly when work completes?
  • Long operations use BGProcessingTask with requiresExternalPower?
  • Background modes in Info.plist limited to what's actually needed?
  • Audio session deactivated when not playing?
  • EMRCA principles followed?

Display/GPU Audit

  • Dark Mode supported (70% OLED power savings)?
  • Animations stopped when view not visible?
  • Secondary animations use appropriate frame rates?
  • Blur effects minimized or removed?
  • Metal rendering has frame limiting?

Disk I/O Audit

  • Writes batched instead of frequent small writes?
  • SQLite using WAL journaling mode?
  • Avoiding rapid file creation/deletion?
  • Using SwiftData/Core Data instead of serialized files for frequent updates?

Pressure Scenarios

Scenario 1: "Just poll every 5 seconds for real-time updates"

The temptation: "Push notifications are complex. Polling is simpler."

The reality:

  • Polling every 5 seconds: Radio active 100% of time
  • Push notifications: Radio active only when data changes
  • Users WILL see your app at top of Battery Settings
  • App Store reviews WILL mention "battery hog"

Time cost comparison:

  • Implement polling: 30 minutes
  • Implement push: 2-4 hours
  • Fix bad reviews + reputation damage: Weeks

Pushback template: "Push notification setup takes a few hours, but polling will guarantee we're at the top of Battery Settings. Users actively uninstall apps that drain battery. The 2-hour investment prevents ongoing reputation damage."


Scenario 2: "Use continuous location for best accuracy"

The temptation: "Users expect accurate location. Let's use kCLLocationAccuracyBest."

The reality:

  • kCLLocationAccuracyBest: GPS + WiFi + Cellular triangulation = massive drain
  • kCLLocationAccuracyHundredMeters: Good enough for 95% of use cases
  • Location icon in status bar = users checking Battery Settings

Time cost comparison:

  • Implement high accuracy: 10 minutes
  • Debug "why does my app drain battery" complaints: Hours
  • Refactor to appropriate accuracy: 30 minutes

Pushback template: "100-meter accuracy is sufficient for [use case]. Navigation apps like Google Maps need best accuracy, but we're showing [store locations / weather / general area]. The accuracy difference is imperceptible to users, but battery difference is massive."


Scenario 3: "Keep animations running, users expect smooth UI"

The temptation: "Animations make the app feel alive and polished."

The reality:

  • Animations running when view not visible = pure waste
  • High frame rate secondary animations = GPU drain
  • GPU power is significant portion of total device power

Time cost comparison:

  • Add animation: 15 minutes
  • Add visibility checks: 5 minutes extra
  • Debug "phone gets hot" reports: Hours

Pushback template: "We can keep the animation, but should pause it when the view isn't visible. This is a 5-minute change that prevents GPU drain when users aren't looking at the screen."


Scenario 4: "Ship now, optimize later"

The temptation: "Energy optimization is polish. We can do it in v1.1."

The reality:

  • Battery drain is immediately visible to users
  • First impressions drive reviews
  • "Battery hog" reputation is hard to shake
  • Power Profiler baseline takes 15 minutes

Time cost comparison:

  • Power Profiler check before launch: 15 minutes
  • Fix energy issues post-launch: Days (plus reputation damage)
  • Regain user trust: Months

Pushback template: "A 15-minute Power Profiler session before launch catches major energy issues. If we ship with battery problems, users will see us at top of Battery Settings on day one and leave 1-star reviews. Let me do a quick check — it's faster than damage control."


Real-World Examples

Example 1: Video Streaming App with Eager Loading (WWDC25-226)

Symptom: CPU power impact jumped from 1 to 21 when opening Library pane. UI hung.

Diagnosis using Power Profiler:

  1. Recorded trace while opening Library pane
  2. CPU Power Impact lane showed massive spike
  3. Time Profiler showed VideoCardView body called hundreds of times
  4. Root cause: VStack creating ALL video thumbnails upfront

Fix:

// Before: VStack (eager)
VStack {
    ForEach(videos) { video in
        VideoCardView(video: video)
    }
}

// After: LazyVStack (on-demand)
LazyVStack {
    ForEach(videos) { video in
        VideoCardView(video: video)
    }
}

Result: CPU power impact dropped from 21 to 4.3. UI no longer hung.


Example 2: Location-Based Suggestions with Repeated Parsing (WWDC25-226)

Symptom: User commuting reported massive battery drain. Developer couldn't reproduce at desk.

Diagnosis using on-device Power Profiler:

  1. User collected trace during commute (Settings → Developer → Performance Trace)
  2. Trace showed periodic CPU spikes correlating with movement
  3. Time Profiler showed videoSuggestionsForLocation consuming CPU
  4. Root cause: JSON file parsed on EVERY location update

Fix:

// Before: Parse on every call
func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
    let data = try? Data(contentsOf: rulesFileURL)
    let rules = try? JSONDecoder().decode([RecommendationRule].self, from: data)
    return filteredVideos(using: rules)
}

// After: Parse once, cache
private lazy var cachedRules: [RecommendationRule] = {
    let data = try? Data(contentsOf: rulesFileURL)
    return (try? JSONDecoder().decode([RecommendationRule].self, from: data)) ?? []
}()

func videoSuggestionsForLocation(_ location: CLLocation) -> [Video] {
    return filteredVideos(using: cachedRules)
}

Result: Eliminated CPU spikes during movement. Battery drain resolved.


Example 3: Music App with Always-Active Audio Session

Symptom: App drains battery even when not playing music.

Diagnosis:

  1. Power Profiler showed sustained background CPU activity
  2. Audio session remained active after playback stopped
  3. System kept audio hardware powered on

Fix:

// Before: Never deactivate
func playTrack(_ track: Track) {
    try? AVAudioSession.sharedInstance().setActive(true)
    player.play()
}

func stopPlayback() {
    player.stop()
    // Audio session still active!
}

// After: Deactivate when done
func stopPlayback() {
    player.stop()
    try? AVAudioSession.sharedInstance().setActive(false, options: .notifyOthersOnDeactivation)
}

// Even better: Use AVAudioEngine auto-shutdown
let engine = AVAudioEngine()
engine.isAutoShutdownEnabled = true  // Automatically powers down when idle

Result: Background audio hardware powered down. Battery drain eliminated.


Responding to Low Power Mode

Detect and adapt when user enables Low Power Mode:

// Check current state
if ProcessInfo.processInfo.isLowPowerModeEnabled {
    reduceEnergyUsage()
}

// React to changes
NotificationCenter.default.publisher(for: .NSProcessInfoPowerStateDidChange)
    .sink { [weak self] _ in
        if ProcessInfo.processInfo.isLowPowerModeEnabled {
            self?.reduceEnergyUsage()
        } else {
            self?.restoreNormalOperation()
        }
    }
    .store(in: &cancellables)

func reduceEnergyUsage() {
    // Pause optional activities
    // Reduce animation frame rates
    // Increase timer intervals
    // Defer network requests
    // Stop location updates if not critical
}

Monitoring Energy in Production

MetricKit Setup

import MetricKit

class EnergyMetricsManager: NSObject, MXMetricManagerSubscriber {
    static let shared = EnergyMetricsManager()

    func startMonitoring() {
        MXMetricManager.shared.add(self)
    }

    func didReceive(_ payloads: [MXMetricPayload]) {
        for payload in payloads {
            if let cpuMetrics = payload.cpuMetrics {
                // Monitor CPU time
                let foregroundCPU = cpuMetrics.cumulativeCPUTime
                logMetric("foreground_cpu", value: foregroundCPU)
            }

            if let locationMetrics = payload.locationActivityMetrics {
                // Monitor location usage
                let backgroundLocation = locationMetrics.cumulativeBackgroundLocationTime
                logMetric("background_location", value: backgroundLocation)
            }
        }
    }
}

Xcode Organizer

Check Battery Usage pane in Xcode Organizer for field data:

  • Foreground vs background energy breakdown
  • Category breakdown (Audio, Networking, Processing, Display, etc.)
  • Version comparison to detect regressions

Quick Reference

Power Profiler Workflow

1. Connect device wirelessly
2. Product → Profile → Blank → Add Power Profiler
3. Record 2-3 minutes of usage
4. Identify dominant subsystem (CPU/GPU/Network/Display)
5. Apply targeted fix from patterns above
6. Record again to verify improvement

Key Energy Savings

Optimization Potential Savings
Dark Mode on OLED Up to 70% display power
Frame rate alignment Up to 20% GPU power
Push vs poll 100x network efficiency
Location accuracy reduction 50-90% GPS power
Timer tolerance Significant CPU savings
Lazy loading Eliminates startup CPU spikes

Related Skills

  • axiom-energy-ref — Complete API reference with all code examples
  • axiom-energy-diag — Symptom-based troubleshooting decision trees
  • axiom-background-processing — Background task mechanics (why tasks don't run)
  • axiom-performance-profiling — General Instruments workflows
  • axiom-memory-debugging — Memory leak diagnosis (often related to energy)
  • axiom-networking — Network optimization patterns

WWDC Sessions

  • WWDC25-226 "Profile and optimize power usage in your app" — Power Profiler workflow
  • WWDC25-227 "Finish tasks in the background" — BGContinuedProcessingTask, EMRCA
  • WWDC22-10083 "Power down: Improve battery consumption" — Dark Mode, frame rates, deferral
  • WWDC20-10095 "The Push Notifications primer" — Push vs poll
  • WWDC19-417 "Improving Battery Life and Performance" — MetricKit

Last Updated: 2025-12-26 Platforms: iOS 26+, iPadOS 26+ Status: Production-ready energy optimization patterns