axiom-energy-ref

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Energy Optimization Reference

iOS能源优化参考文档

Complete API reference for iOS energy optimization, with code examples from WWDC sessions and Apple documentation.
Related skills:
axiom-energy
(decision trees, patterns),
axiom-energy-diag
(troubleshooting)

这是一份完整的iOS能源优化API参考文档,包含WWDC会话和Apple官方文档中的代码示例。
相关技能
axiom-energy
(决策树、模式识别)、
axiom-energy-diag
(故障排查)

Part 1: Power Profiler Workflow

第一部分:Power Profiler工作流

Recording a Trace with Instruments

使用Instruments录制性能追踪

Tethered Recording (Connected to Mac)

有线录制(连接Mac)

1. Connect iPhone wirelessly to Xcode
   - Xcode → Window → Devices and Simulators
   - Enable "Connect via network" for your device

2. Profile your app
   - Xcode → Product → Profile (Cmd+I)
   - Select Blank template
   - Click "+" → Add "Power Profiler"
   - Optionally add "CPU Profiler" for correlation

3. Record
   - Select your app from target dropdown
   - Click Record (red button)
   - Use app normally for 2-3 minutes
   - Click Stop

4. Analyze
   - Expand Power Profiler track
   - Examine per-app lanes: CPU, GPU, Display, Network
Important: Use wireless debugging. When device is charging via cable, system power usage shows 0.
1. 将iPhone无线连接到Xcode
   - Xcode → 窗口 → 设备和模拟器
   - 为你的设备启用“通过网络连接”

2. 分析你的应用
   - Xcode → 产品 → 分析(Cmd+I)
   - 选择空白模板
   - 点击“+” → 添加“Power Profiler”
   - 可选择性添加“CPU Profiler”用于关联分析

3. 录制
   - 从目标下拉菜单中选择你的应用
   - 点击录制(红色按钮)
   - 正常使用应用2-3分钟
   - 点击停止

4. 分析
   - 展开Power Profiler轨道
   - 检查各应用的细分项:CPU、GPU、显示屏、网络
重要提示:使用无线调试。当设备通过线缆充电时,系统功耗会显示为0。

On-Device Recording (Without Mac)

设备端录制(无需Mac)

From WWDC25-226: Capture traces in real-world conditions.
1. Enable Developer Mode
   Settings → Privacy & Security → Developer Mode → Enable

2. Enable Performance Trace
   Settings → Developer → Performance Trace → Enable
   Set tracing mode to "Power Profiler"
   Toggle ON your app in the app list

3. Add Control Center shortcut
   Control Center → Tap "+" → Add a Control → Performance Trace

4. Record
   Swipe down → Tap Performance Trace icon → Start
   Use app (can record up to 10 hours)
   Tap Performance Trace icon → Stop

5. Share trace
   Settings → Developer → Performance Trace
   Tap Share button next to trace file
   AirDrop to Mac or email to developer
来自WWDC25-226:在真实场景下捕获性能追踪数据。
1. 启用开发者模式
   设置 → 隐私与安全性 → 开发者模式 → 启用

2. 启用性能追踪
   设置 → 开发者 → 性能追踪 → 启用
   将追踪模式设置为“Power Profiler”
   在应用列表中开启你的应用

3. 添加控制中心快捷方式
   控制中心 → 点击“+” → 添加“性能追踪”控件

4. 录制
   向下滑动 → 点击性能追踪图标 → 开始
   使用应用(最长可录制10小时)
   点击性能追踪图标 → 停止

5. 分享追踪数据
   设置 → 开发者 → 性能追踪
   点击追踪文件旁的分享按钮
   通过AirDrop发送到Mac或邮件发送给开发者

Interpreting Power Profiler Metrics

解读Power Profiler指标

LaneMeaningWhat High Values Indicate
System PowerOverall battery drain rateGeneral energy consumption
CPU Power ImpactProcessor activity scoreComputation, timers, parsing
GPU Power ImpactGraphics rendering scoreAnimations, blur, Metal
Display Power ImpactScreen power usageBrightness, content type
Network Power ImpactRadio activity scoreRequests, downloads, polling
Key insight: Values are scores for comparison, not absolute measurements. Compare before/after traces on the same device.
轨道含义高数值表明
系统功耗整体电池消耗速率总体能源消耗情况
CPU功耗影响处理器活动评分计算任务、计时器、解析操作
GPU功耗影响图形渲染评分动画、模糊效果、Metal框架使用
显示屏功耗影响屏幕功耗亮度、显示内容类型
网络功耗影响无线模块活动评分请求、下载、轮询操作
关键要点:这些数值是用于对比的评分,而非绝对测量值。请在同一设备上对比优化前后的追踪数据。

Comparing Before/After (Example from WWDC25-226)

优化前后对比(来自WWDC25-226示例)

swift
// Before optimization: CPU Power Impact = 21
VStack {
    ForEach(videos) { video in
        VideoCardView(video: video)
    }
}

// After optimization: CPU Power Impact = 4.3
LazyVStack {
    ForEach(videos) { video in
        VideoCardView(video: video)
    }
}

swift
// 优化前:CPU功耗影响 = 21
VStack {
    ForEach(videos) { video in
        VideoCardView(video: video)
    }
}

// 优化后:CPU功耗影响 = 4.3
LazyVStack {
    ForEach(videos) { video in
        VideoCardView(video: video)
    }
}

Part 2: Timer Efficiency APIs

第二部分:计时器效率API

NSTimer with Tolerance

带容差的NSTimer

swift
// Basic timer with tolerance
let timer = Timer.scheduledTimer(
    withTimeInterval: 1.0,
    repeats: true
) { [weak self] _ in
    self?.updateUI()
}
timer.tolerance = 0.1  // 10% minimum recommended

// Add to run loop (if not using scheduledTimer)
RunLoop.current.add(timer, forMode: .common)

// Always invalidate when done
deinit {
    timer.invalidate()
}
swift
// 基础带容差计时器
let timer = Timer.scheduledTimer(
    withTimeInterval: 1.0,
    repeats: true
) { [weak self] _ in
    self?.updateUI()
}
timer.tolerance = 0.1  // 建议最小容差为10%

// 添加到运行循环(如果未使用scheduledTimer)
RunLoop.current.add(timer, forMode: .common)

// 完成后务必失效
deinit {
    timer.invalidate()
}

Combine Timer Publisher

Combine Timer Publisher

swift
import Combine

class ViewModel: ObservableObject {
    private var cancellables = Set<AnyCancellable>()

    func startPolling() {
        Timer.publish(every: 1.0, tolerance: 0.1, on: .main, in: .default)
            .autoconnect()
            .sink { [weak self] _ in
                self?.refresh()
            }
            .store(in: &cancellables)
    }

    func stopPolling() {
        cancellables.removeAll()
    }
}
swift
import Combine

class ViewModel: ObservableObject {
    private var cancellables = Set<AnyCancellable>()

    func startPolling() {
        Timer.publish(every: 1.0, tolerance: 0.1, on: .main, in: .default)
            .autoconnect()
            .sink { [weak self] _ in
                self?.refresh()
            }
            .store(in: &cancellables)
    }

    func stopPolling() {
        cancellables.removeAll()
    }
}

Dispatch Timer Source (Low-Level)

底层Dispatch Timer Source

From Energy Efficiency Guide:
swift
let queue = DispatchQueue(label: "com.app.timer")
let timer = DispatchSource.makeTimerSource(queue: queue)

// Set interval with leeway (tolerance)
timer.schedule(
    deadline: .now(),
    repeating: .seconds(1),
    leeway: .milliseconds(100)  // 10% tolerance
)

timer.setEventHandler { [weak self] in
    self?.performWork()
}

timer.resume()

// Cancel when done
timer.cancel()
来自《能源效率指南》:
swift
let queue = DispatchQueue(label: "com.app.timer")
let timer = DispatchSource.makeTimerSource(queue: queue)

// 设置带误差范围的时间间隔(容差)
timer.schedule(
    deadline: .now(),
    repeating: .seconds(1),
    leeway: .milliseconds(100)  // 10%容差
)

timer.setEventHandler { [weak self] in
    self?.performWork()
}

timer.resume()

// 完成后取消
timer.cancel()

Event-Driven Alternative to Timers

计时器的事件驱动替代方案

From Energy Efficiency Guide: Prefer dispatch sources over polling.
swift
// Monitor file changes instead of polling
let fileDescriptor = open(filePath.path, O_EVTONLY)
let source = DispatchSource.makeFileSystemObjectSource(
    fileDescriptor: fileDescriptor,
    eventMask: [.write, .delete],
    queue: .main
)

source.setEventHandler { [weak self] in
    self?.handleFileChange()
}

source.setCancelHandler {
    close(fileDescriptor)
}

source.resume()

来自《能源效率指南》:优先使用调度源而非轮询。
swift
// 监控文件变化而非轮询
let fileDescriptor = open(filePath.path, O_EVTONLY)
let source = DispatchSource.makeFileSystemObjectSource(
    fileDescriptor: fileDescriptor,
    eventMask: [.write, .delete],
    queue: .main
)

source.setEventHandler { [weak self] in
    self?.handleFileChange()
}

source.setCancelHandler {
    close(fileDescriptor)
}

source.resume()

Part 3: Network Efficiency APIs

第三部分:网络效率API

URLSession Configuration

URLSession配置

swift
// Standard configuration with energy-conscious settings
let config = URLSessionConfiguration.default
config.waitsForConnectivity = true  // Don't fail immediately
config.allowsExpensiveNetworkAccess = false  // Prefer WiFi
config.allowsConstrainedNetworkAccess = false  // Respect Low Data Mode

let session = URLSession(configuration: config)
swift
// 具备节能设置的标准配置
let config = URLSessionConfiguration.default
config.waitsForConnectivity = true  // 不立即失败
config.allowsExpensiveNetworkAccess = false  // 优先使用WiFi
config.allowsConstrainedNetworkAccess = false  // 遵循低数据模式

let session = URLSession(configuration: config)

Discretionary Background Downloads

可自主调度的后台下载

From WWDC22-10083:
swift
// Background session for non-urgent downloads
let config = URLSessionConfiguration.background(
    withIdentifier: "com.app.downloads"
)
config.isDiscretionary = true  // System chooses optimal time
config.sessionSendsLaunchEvents = true

// Set timeouts
config.timeoutIntervalForResource = 24 * 60 * 60  // 24 hours
config.timeoutIntervalForRequest = 60

let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)

// Create download task with scheduling hints
let task = session.downloadTask(with: url)
task.earliestBeginDate = Date(timeIntervalSinceNow: 2 * 60 * 60)  // 2 hours from now
task.countOfBytesClientExpectsToSend = 200  // Small request
task.countOfBytesClientExpectsToReceive = 500_000  // 500KB response

task.resume()
来自WWDC22-10083:
swift
// 用于非紧急下载的后台会话
let config = URLSessionConfiguration.background(
    withIdentifier: "com.app.downloads"
)
config.isDiscretionary = true  // 由系统选择最佳时间
config.sessionSendsLaunchEvents = true

// 设置超时时间
config.timeoutIntervalForResource = 24 * 60 * 60  // 24小时
config.timeoutIntervalForRequest = 60

let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)

// 创建带调度提示的下载任务
let task = session.downloadTask(with: url)
task.earliestBeginDate = Date(timeIntervalSinceNow: 2 * 60 * 60)  // 2小时后开始
task.countOfBytesClientExpectsToSend = 200  // 小请求
task.countOfBytesClientExpectsToReceive = 500_000  // 500KB响应

task.resume()

Background Session Delegate

后台会话代理

swift
class DownloadDelegate: NSObject, URLSessionDownloadDelegate {
    func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didFinishDownloadingTo location: URL
    ) {
        // Move file from temp location
        let destination = FileManager.default.urls(
            for: .documentDirectory,
            in: .userDomainMask
        )[0].appendingPathComponent("downloaded.data")

        try? FileManager.default.moveItem(at: location, to: destination)
    }

    func urlSessionDidFinishEvents(
        forBackgroundURLSession session: URLSession
    ) {
        // Notify app delegate to call completion handler
        DispatchQueue.main.async {
            if let handler = AppDelegate.shared.backgroundCompletionHandler {
                handler()
                AppDelegate.shared.backgroundCompletionHandler = nil
            }
        }
    }
}

swift
class DownloadDelegate: NSObject, URLSessionDownloadDelegate {
    func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didFinishDownloadingTo location: URL
    ) {
        // 将文件从临时位置移动
        let destination = FileManager.default.urls(
            for: .documentDirectory,
            in: .userDomainMask
        )[0].appendingPathComponent("downloaded.data")

        try? FileManager.default.moveItem(at: location, to: destination)
    }

    func urlSessionDidFinishEvents(
        forBackgroundURLSession session: URLSession
    ) {
        // 通知应用代理调用完成处理程序
        DispatchQueue.main.async {
            if let handler = AppDelegate.shared.backgroundCompletionHandler {
                handler()
                AppDelegate.shared.backgroundCompletionHandler = nil
            }
        }
    }
}

Part 4: Location Efficiency APIs

第四部分:位置效率API

CLLocationManager Configuration

CLLocationManager配置

swift
import CoreLocation

class LocationService: NSObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()

    func configure() {
        manager.delegate = self

        // Use appropriate accuracy
        manager.desiredAccuracy = kCLLocationAccuracyHundredMeters

        // Reduce update frequency
        manager.distanceFilter = 100  // Update every 100 meters

        // Allow indicator pause when stationary
        manager.pausesLocationUpdatesAutomatically = true

        // For background updates (if needed)
        manager.allowsBackgroundLocationUpdates = true
        manager.showsBackgroundLocationIndicator = true
    }

    func startTracking() {
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
    }

    func startSignificantChangeTracking() {
        // Much more energy efficient for background
        manager.startMonitoringSignificantLocationChanges()
    }

    func stopTracking() {
        manager.stopUpdatingLocation()
        manager.stopMonitoringSignificantLocationChanges()
    }
}
swift
import CoreLocation

class LocationService: NSObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()

    func configure() {
        manager.delegate = self

        // 使用合适的精度
        manager.desiredAccuracy = kCLLocationAccuracyHundredMeters

        // 降低更新频率
        manager.distanceFilter = 100  // 每移动100米更新一次

        // 静止时允许暂停位置更新
        manager.pausesLocationUpdatesAutomatically = true

        // 如需后台更新
        manager.allowsBackgroundLocationUpdates = true
        manager.showsBackgroundLocationIndicator = true
    }

    func startTracking() {
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
    }

    func startSignificantChangeTracking() {
        // 后台使用时更节能
        manager.startMonitoringSignificantLocationChanges()
    }

    func stopTracking() {
        manager.stopUpdatingLocation()
        manager.stopMonitoringSignificantLocationChanges()
    }
}

iOS 26+ CLLocationUpdate (Modern Async API)

iOS 26+ CLLocationUpdate(现代异步API)

swift
import CoreLocation

func trackLocation() async throws {
    for try await update in CLLocationUpdate.liveUpdates() {
        // Check if device became stationary
        if update.stationary {
            // System pauses updates automatically
            // Consider switching to region monitoring
            break
        }

        if let location = update.location {
            handleLocation(location)
        }
    }
}
swift
import CoreLocation

func trackLocation() async throws {
    for try await update in CLLocationUpdate.liveUpdates() {
        // 检查设备是否静止
        if update.stationary {
            // 系统会自动暂停更新
            // 考虑切换到区域监控
            break
        }

        if let location = update.location {
            handleLocation(location)
        }
    }
}

CLMonitor for Significant Changes

CLMonitor用于显著位置变化

swift
import CoreLocation

func setupRegionMonitoring() async {
    let monitor = CLMonitor("significant-changes")

    // Add condition to monitor
    let condition = CLMonitor.CircularGeographicCondition(
        center: currentLocation.coordinate,
        radius: 500  // 500 meter radius
    )
    await monitor.add(condition, identifier: "home-region")

    // React to events
    for try await event in monitor.events {
        switch event.state {
        case .satisfied:
            // Entered region
            handleRegionEntry()
        case .unsatisfied:
            // Exited region
            handleRegionExit()
        default:
            break
        }
    }
}
swift
import CoreLocation

func setupRegionMonitoring() async {
    let monitor = CLMonitor("significant-changes")

    // 添加要监控的条件
    let condition = CLMonitor.CircularGeographicCondition(
        center: currentLocation.coordinate,
        radius: 500  // 500米半径
    )
    await monitor.add(condition, identifier: "home-region")

    // 响应事件
    for try await event in monitor.events {
        switch event.state {
        case .satisfied:
            // 进入区域
            handleRegionEntry()
        case .unsatisfied:
            // 离开区域
            handleRegionExit()
        default:
            break
        }
    }
}

Location Accuracy Options

位置精度选项

ConstantAccuracyBattery ImpactUse Case
kCLLocationAccuracyBestForNavigation
~1mExtremeTurn-by-turn only
kCLLocationAccuracyBest
~10mVery HighFitness tracking
kCLLocationAccuracyNearestTenMeters
~10mHighPrecise positioning
kCLLocationAccuracyHundredMeters
~100mMediumStore locators
kCLLocationAccuracyKilometer
~1kmLowWeather, general
kCLLocationAccuracyThreeKilometers
~3kmVery LowRegional content

常量精度电池影响使用场景
kCLLocationAccuracyBestForNavigation
~1米极高仅用于逐向导航
kCLLocationAccuracyBest
~10米很高健身追踪
kCLLocationAccuracyNearestTenMeters
~10米精确定位
kCLLocationAccuracyHundredMeters
~100米中等商店定位器
kCLLocationAccuracyKilometer
~1公里天气、通用场景
kCLLocationAccuracyThreeKilometers
~3公里极低区域内容推送

Part 5: Background Execution APIs

第五部分:后台执行API

beginBackgroundTask (Short Tasks)

beginBackgroundTask(短任务)

swift
class AppDelegate: UIResponder, UIApplicationDelegate {
    var backgroundTask: UIBackgroundTaskIdentifier = .invalid

    func applicationDidEnterBackground(_ application: UIApplication) {
        backgroundTask = application.beginBackgroundTask(withName: "Save State") {
            // Expiration handler - clean up
            self.endBackgroundTask()
        }

        // Perform quick work
        saveState()

        // End immediately when done
        endBackgroundTask()
    }

    private func endBackgroundTask() {
        guard backgroundTask != .invalid else { return }
        UIApplication.shared.endBackgroundTask(backgroundTask)
        backgroundTask = .invalid
    }
}
swift
class AppDelegate: UIResponder, UIApplicationDelegate {
    var backgroundTask: UIBackgroundTaskIdentifier = .invalid

    func applicationDidEnterBackground(_ application: UIApplication) {
        backgroundTask = application.beginBackgroundTask(withName: "保存状态") {
            // 过期处理程序 - 清理资源
            self.endBackgroundTask()
        }

        // 执行快速任务
        saveState()

        // 完成后立即结束
        endBackgroundTask()
    }

    private func endBackgroundTask() {
        guard backgroundTask != .invalid else { return }
        UIApplication.shared.endBackgroundTask(backgroundTask)
        backgroundTask = .invalid
    }
}

BGAppRefreshTask

BGAppRefreshTask

swift
import BackgroundTasks

// Register at app launch
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.app.refresh",
        using: nil
    ) { task in
        self.handleAppRefresh(task: task as! BGAppRefreshTask)
    }

    return true
}

// Schedule refresh
func scheduleAppRefresh() {
    let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh")
    request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)  // 15 min

    try? BGTaskScheduler.shared.submit(request)
}

// Handle refresh
func handleAppRefresh(task: BGAppRefreshTask) {
    scheduleAppRefresh()  // Schedule next refresh

    let fetchTask = Task {
        do {
            let hasNewData = try await fetchLatestData()
            task.setTaskCompleted(success: hasNewData)
        } catch {
            task.setTaskCompleted(success: false)
        }
    }

    task.expirationHandler = {
        fetchTask.cancel()
    }
}
swift
import BackgroundTasks

// 在应用启动时注册
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.app.refresh",
        using: nil
    ) { task in
        self.handleAppRefresh(task: task as! BGAppRefreshTask)
    }

    return true
}

// 调度刷新任务
func scheduleAppRefresh() {
    let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh")
    request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)  // 15分钟后

    try? BGTaskScheduler.shared.submit(request)
}

// 处理刷新任务
func handleAppRefresh(task: BGAppRefreshTask) {
    scheduleAppRefresh()  // 调度下一次刷新

    let fetchTask = Task {
        do {
            let hasNewData = try await fetchLatestData()
            task.setTaskCompleted(success: hasNewData)
        } catch {
            task.setTaskCompleted(success: false)
        }
    }

    task.expirationHandler = {
        fetchTask.cancel()
    }
}

BGProcessingTask

BGProcessingTask

swift
import BackgroundTasks

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

// Schedule with requirements
func scheduleMaintenance() {
    let request = BGProcessingTaskRequest(identifier: "com.app.maintenance")
    request.requiresNetworkConnectivity = true
    request.requiresExternalPower = true  // Only when charging

    try? BGTaskScheduler.shared.submit(request)
}

// Handle
func handleMaintenance(task: BGProcessingTask) {
    let operation = MaintenanceOperation()

    task.expirationHandler = {
        operation.cancel()
    }

    operation.completionBlock = {
        task.setTaskCompleted(success: !operation.isCancelled)
    }

    OperationQueue.main.addOperation(operation)
}
swift
import BackgroundTasks

// 注册任务
BGTaskScheduler.shared.register(
    forTaskWithIdentifier: "com.app.maintenance",
    using: nil
) { task in
    self.handleMaintenance(task: task as! BGProcessingTask)
}

// 带需求条件调度任务
func scheduleMaintenance() {
    let request = BGProcessingTaskRequest(identifier: "com.app.maintenance")
    request.requiresNetworkConnectivity = true
    request.requiresExternalPower = true  // 仅在充电时执行

    try? BGTaskScheduler.shared.submit(request)
}

// 处理维护任务
func handleMaintenance(task: BGProcessingTask) {
    let operation = MaintenanceOperation()

    task.expirationHandler = {
        operation.cancel()
    }

    operation.completionBlock = {
        task.setTaskCompleted(success: !operation.isCancelled)
    }

    OperationQueue.main.addOperation(operation)
}

iOS 26+ BGContinuedProcessingTask

iOS 26+ BGContinuedProcessingTask

From WWDC25-227: Continue user-initiated tasks with system UI.
swift
import BackgroundTasks

// Info.plist: Add identifier to BGTaskSchedulerPermittedIdentifiers
// "com.app.export" or "com.app.exports.*" for wildcards

// Register handler (can be dynamic, not just at launch)
func setupExportHandler() {
    BGTaskScheduler.shared.register("com.app.export") { task in
        let continuedTask = task as! BGContinuedProcessingTask

        var shouldContinue = true
        continuedTask.expirationHandler = {
            shouldContinue = false
        }

        // Report progress
        continuedTask.progress.totalUnitCount = 100
        continuedTask.progress.completedUnitCount = 0

        // Perform work
        for i in 0..<100 {
            guard shouldContinue else { break }

            performExportStep(i)
            continuedTask.progress.completedUnitCount = Int64(i + 1)
        }

        continuedTask.setTaskCompleted(success: shouldContinue)
    }
}

// Submit request
func startExport() {
    let request = BGContinuedProcessingTaskRequest(
        identifier: "com.app.export",
        title: "Exporting Photos",
        subtitle: "0 of 100 photos"
    )

    // Submission strategy
    request.strategy = .fail  // Fail if can't start immediately
    // or default: queue if can't start

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        // Handle submission failure
        showExportNotAvailable()
    }
}
来自WWDC25-227:通过系统UI继续用户发起的任务。
swift
import BackgroundTasks

// Info.plist: 将标识符添加到BGTaskSchedulerPermittedIdentifiers
// "com.app.export" 或 "com.app.exports.*" 用于通配符

// 注册处理程序(可动态注册,无需仅在启动时注册)
func setupExportHandler() {
    BGTaskScheduler.shared.register("com.app.export") { task in
        let continuedTask = task as! BGContinuedProcessingTask

        var shouldContinue = true
        continuedTask.expirationHandler = {
            shouldContinue = false
        }

        // 报告进度
        continuedTask.progress.totalUnitCount = 100
        continuedTask.progress.completedUnitCount = 0

        // 执行任务
        for i in 0..<100 {
            guard shouldContinue else { break }

            performExportStep(i)
            continuedTask.progress.completedUnitCount = Int64(i + 1)
        }

        continuedTask.setTaskCompleted(success: shouldContinue)
    }
}

// 提交任务请求
func startExport() {
    let request = BGContinuedProcessingTaskRequest(
        identifier: "com.app.export",
        title: "导出照片",
        subtitle: "0/100张照片"
    )

    // 提交策略
    request.strategy = .fail  // 若无法立即启动则失败
    // 或默认:若无法立即启动则加入队列

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        // 处理提交失败
        showExportNotAvailable()
    }
}

EMRCA Principles (from WWDC25-227)

EMRCA原则(来自WWDC25-227)

Background tasks must be:
PrincipleMeaningImplementation
EfficientLightweight, purpose-drivenDo one thing well
MinimalKeep work to minimumDon't expand scope
ResilientSave progress, handle expirationCheckpoint frequently
CourteousHonor preferencesCheck Low Power Mode
AdaptiveWork with systemDon't fight constraints

后台任务必须遵循:
原则含义实现方式
Efficient(高效)轻量、目标明确专注完成一件事
Minimal(最小化)将工作量降至最低不扩大任务范围
Resilient(弹性)保存进度、处理过期频繁检查点
Courteous(友好)尊重用户偏好检查低电量模式
Adaptive(自适应)配合系统工作不违背系统限制

Part 6: Display & GPU Efficiency APIs

第六部分:显示屏与GPU效率API

Dark Mode Support

深色模式支持

swift
// Check current appearance
let isDarkMode = traitCollection.userInterfaceStyle == .dark

// React to appearance changes
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
        updateColorsForAppearance()
    }
}

// Use dynamic colors
let dynamicColor = UIColor { traitCollection in
    switch traitCollection.userInterfaceStyle {
    case .dark:
        return UIColor.black  // OLED: True black = pixels off = 0 power
    default:
        return UIColor.white
    }
}
swift
// 检查当前外观模式
let isDarkMode = traitCollection.userInterfaceStyle == .dark

// 响应外观变化
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
        updateColorsForAppearance()
    }
}

// 使用动态颜色
let dynamicColor = UIColor { traitCollection in
    switch traitCollection.userInterfaceStyle {
    case .dark:
        return UIColor.black  // OLED:纯黑 = 像素关闭 = 0功耗
    default:
        return UIColor.white
    }
}

Frame Rate Control with CADisplayLink

使用CADisplayLink控制帧率

From WWDC22-10083:
swift
class AnimationController {
    private var displayLink: CADisplayLink?

    func startAnimation() {
        displayLink = CADisplayLink(target: self, selector: #selector(update))

        // Control frame rate
        displayLink?.preferredFrameRateRange = CAFrameRateRange(
            minimum: 10,   // Minimum acceptable
            maximum: 30,   // Maximum needed
            preferred: 30  // Ideal rate
        )

        displayLink?.add(to: .current, forMode: .default)
    }

    @objc private func update(_ displayLink: CADisplayLink) {
        // Update animation
        updateAnimationFrame()
    }

    func stopAnimation() {
        displayLink?.invalidate()
        displayLink = nil
    }
}
来自WWDC22-10083:
swift
class AnimationController {
    private var displayLink: CADisplayLink?

    func startAnimation() {
        displayLink = CADisplayLink(target: self, selector: #selector(update))

        // 控制帧率
        displayLink?.preferredFrameRateRange = CAFrameRateRange(
            minimum: 10,   // 最低可接受帧率
            maximum: 30,   // 所需最高帧率
            preferred: 30  // 理想帧率
        )

        displayLink?.add(to: .current, forMode: .default)
    }

    @objc private func update(_ displayLink: CADisplayLink) {
        // 更新动画帧
        updateAnimationFrame()
    }

    func stopAnimation() {
        displayLink?.invalidate()
        displayLink = nil
    }
}

Stop Animations When Not Visible

不可见时停止动画

swift
class AnimatedViewController: UIViewController {
    private var animator: UIViewPropertyAnimator?

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        startAnimations()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        stopAnimations()  // Critical for energy
    }

    private func stopAnimations() {
        animator?.stopAnimation(true)
        animator = nil
    }
}

swift
class AnimatedViewController: UIViewController {
    private var animator: UIViewPropertyAnimator?

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        startAnimations()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        stopAnimations()  // 对节能至关重要
    }

    private func stopAnimations() {
        animator?.stopAnimation(true)
        animator = nil
    }
}

Part 7: Disk I/O Efficiency APIs

第七部分:磁盘I/O效率API

Batch Writes

批量写入

swift
// BAD: Multiple small writes
for item in items {
    let data = try JSONEncoder().encode(item)
    try data.write(to: fileURL)  // Writes each item separately
}

// GOOD: Single batched write
let allData = try JSONEncoder().encode(items)
try allData.write(to: fileURL)  // One write operation
swift
// 不良实践:多次小写入
for item in items {
    let data = try JSONEncoder().encode(item)
    try data.write(to: fileURL)  // 单独写入每个条目
}

// 良好实践:单次批量写入
let allData = try JSONEncoder().encode(items)
try allData.write(to: fileURL)  // 一次写入操作

SQLite WAL Mode

SQLite WAL模式

swift
import SQLite3

// Enable Write-Ahead Logging
var db: OpaquePointer?
sqlite3_open(dbPath, &db)

var statement: OpaquePointer?
sqlite3_prepare_v2(db, "PRAGMA journal_mode=WAL", -1, &statement, nil)
sqlite3_step(statement)
sqlite3_finalize(statement)
swift
import SQLite3

// 启用预写日志
var db: OpaquePointer?
sqlite3_open(dbPath, &db)

var statement: OpaquePointer?
sqlite3_prepare_v2(db, "PRAGMA journal_mode=WAL", -1, &statement, nil)
sqlite3_step(statement)
sqlite3_finalize(statement)

XCTStorageMetric for Testing

使用XCTStorageMetric进行测试

swift
import XCTest

class DiskWriteTests: XCTestCase {
    func testDiskWritePerformance() {
        measure(metrics: [XCTStorageMetric()]) {
            // Code that writes to disk
            saveUserData()
        }
    }
}

swift
import XCTest

class DiskWriteTests: XCTestCase {
    func testDiskWritePerformance() {
        measure(metrics: [XCTStorageMetric()]) {
            // 写入磁盘的代码
            saveUserData()
        }
    }
}

Part 8: Low Power Mode & Thermal Response APIs

第八部分:低电量模式与热状态响应API

Low Power Mode Detection

低电量模式检测

swift
import Foundation

class PowerStateManager {
    private var cancellables = Set<AnyCancellable>()

    init() {
        // Check initial state
        updateForPowerState()

        // Observe changes
        NotificationCenter.default.publisher(
            for: .NSProcessInfoPowerStateDidChange
        )
        .sink { [weak self] _ in
            self?.updateForPowerState()
        }
        .store(in: &cancellables)
    }

    private func updateForPowerState() {
        if ProcessInfo.processInfo.isLowPowerModeEnabled {
            reduceEnergyUsage()
        } else {
            restoreNormalOperation()
        }
    }

    private func reduceEnergyUsage() {
        // Increase timer intervals
        // Reduce animation frame rates
        // Defer network requests
        // Stop location updates if not critical
        // Reduce refresh frequency
    }
}
swift
import Foundation

class PowerStateManager {
    private var cancellables = Set<AnyCancellable>()

    init() {
        // 检查初始状态
        updateForPowerState()

        // 监听状态变化
        NotificationCenter.default.publisher(
            for: .NSProcessInfoPowerStateDidChange
        )
        .sink { [weak self] _ in
            self?.updateForPowerState()
        }
        .store(in: &cancellables)
    }

    private func updateForPowerState() {
        if ProcessInfo.processInfo.isLowPowerModeEnabled {
            reduceEnergyUsage()
        } else {
            restoreNormalOperation()
        }
    }

    private func reduceEnergyUsage() {
        // 增加计时器间隔
        // 降低动画帧率
        // 延迟网络请求
        // 若非关键则停止位置更新
        // 降低刷新频率
    }
}

Thermal State Response

热状态响应

swift
import Foundation

class ThermalManager {
    init() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(thermalStateChanged),
            name: ProcessInfo.thermalStateDidChangeNotification,
            object: nil
        )
    }

    @objc private func thermalStateChanged() {
        switch ProcessInfo.processInfo.thermalState {
        case .nominal:
            // Normal operation
            restoreFullFunctionality()

        case .fair:
            // Slightly elevated, minor reduction
            reduceNonEssentialWork()

        case .serious:
            // Significant reduction needed
            suspendBackgroundTasks()
            reduceAnimationQuality()

        case .critical:
            // Maximum reduction
            minimizeAllActivity()
            showThermalWarningIfAppropriate()

        @unknown default:
            break
        }
    }
}

swift
import Foundation

class ThermalManager {
    init() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(thermalStateChanged),
            name: ProcessInfo.thermalStateDidChangeNotification,
            object: nil
        )
    }

    @objc private func thermalStateChanged() {
        switch ProcessInfo.processInfo.thermalState {
        case .nominal:
            // 正常运行
            restoreFullFunctionality()

        case .fair:
            // 温度略有升高,轻微降低负载
            reduceNonEssentialWork()

        case .serious:
            // 需要显著降低负载
            suspendBackgroundTasks()
            reduceAnimationQuality()

        case .critical:
            // 最大限度降低负载
            minimizeAllActivity()
            showThermalWarningIfAppropriate()

        @unknown default:
            break
        }
    }
}

Part 9: MetricKit Monitoring APIs

第九部分:MetricKit监控API

Basic Setup

基础设置

swift
import MetricKit

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

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

    func didReceive(_ payloads: [MXMetricPayload]) {
        for payload in payloads {
            processPayload(payload)
        }
    }

    func didReceive(_ payloads: [MXDiagnosticPayload]) {
        for payload in payloads {
            processDiagnostic(payload)
        }
    }
}
swift
import MetricKit

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

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

    func didReceive(_ payloads: [MXMetricPayload]) {
        for payload in payloads {
            processPayload(payload)
        }
    }

    func didReceive(_ payloads: [MXDiagnosticPayload]) {
        for payload in payloads {
            processDiagnostic(payload)
        }
    }
}

Processing Energy Metrics

处理能源指标

swift
func processPayload(_ payload: MXMetricPayload) {
    // CPU metrics
    if let cpu = payload.cpuMetrics {
        let foregroundTime = cpu.cumulativeCPUTime
        let backgroundTime = cpu.cumulativeCPUInstructions
        logMetric("cpu_foreground", value: foregroundTime)
    }

    // Location metrics
    if let location = payload.locationActivityMetrics {
        let backgroundLocationTime = location.cumulativeBackgroundLocationTime
        logMetric("background_location_seconds", value: backgroundLocationTime)
    }

    // Network metrics
    if let network = payload.networkTransferMetrics {
        let cellularUpload = network.cumulativeCellularUpload
        let cellularDownload = network.cumulativeCellularDownload
        let wifiUpload = network.cumulativeWiFiUpload
        let wifiDownload = network.cumulativeWiFiDownload

        logMetric("cellular_upload", value: cellularUpload)
        logMetric("cellular_download", value: cellularDownload)
    }

    // Disk metrics
    if let disk = payload.diskIOMetrics {
        let writes = disk.cumulativeLogicalWrites
        logMetric("disk_writes", value: writes)
    }

    // GPU metrics
    if let gpu = payload.gpuMetrics {
        let gpuTime = gpu.cumulativeGPUTime
        logMetric("gpu_time", value: gpuTime)
    }
}
swift
func processPayload(_ payload: MXMetricPayload) {
    // CPU指标
    if let cpu = payload.cpuMetrics {
        let foregroundTime = cpu.cumulativeCPUTime
        let backgroundTime = cpu.cumulativeCPUInstructions
        logMetric("cpu_foreground", value: foregroundTime)
    }

    // 位置指标
    if let location = payload.locationActivityMetrics {
        let backgroundLocationTime = location.cumulativeBackgroundLocationTime
        logMetric("background_location_seconds", value: backgroundLocationTime)
    }

    // 网络指标
    if let network = payload.networkTransferMetrics {
        let cellularUpload = network.cumulativeCellularUpload
        let cellularDownload = network.cumulativeCellularDownload
        let wifiUpload = network.cumulativeWiFiUpload
        let wifiDownload = network.cumulativeWiFiDownload

        logMetric("cellular_upload", value: cellularUpload)
        logMetric("cellular_download", value: cellularDownload)
    }

    // 磁盘指标
    if let disk = payload.diskIOMetrics {
        let writes = disk.cumulativeLogicalWrites
        logMetric("disk_writes", value: writes)
    }

    // GPU指标
    if let gpu = payload.gpuMetrics {
        let gpuTime = gpu.cumulativeGPUTime
        logMetric("gpu_time", value: gpuTime)
    }
}

Xcode Organizer Integration

Xcode Organizer集成

View field metrics in Xcode:
  1. Window → Organizer
  2. Select your app
  3. Click "Battery Usage" in sidebar
  4. Compare versions, filter by device/OS
Categories shown:
  • Audio
  • Networking
  • Processing (CPU + GPU)
  • Display
  • Bluetooth
  • Location
  • Camera
  • Torch
  • NFC
  • Other

在Xcode中查看现场指标:
  1. 窗口 → Organizer
  2. 选择你的应用
  3. 点击侧边栏中的“电池使用情况”
  4. 对比版本,按设备/系统版本筛选
显示的分类:
  • 音频
  • 网络
  • 处理(CPU + GPU)
  • 显示屏
  • 蓝牙
  • 位置
  • 相机
  • 手电筒
  • NFC
  • 其他

Part 10: Push Notifications APIs

第十部分:推送通知API

Alert Notifications Setup

提醒通知设置

From WWDC20-10095:
swift
import UserNotifications

class NotificationManager: NSObject, UNUserNotificationCenterDelegate {

    func setup() {
        UNUserNotificationCenter.current().delegate = self
        UIApplication.shared.registerForRemoteNotifications()
    }

    func requestPermission() {
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert, .sound, .badge]
        ) { granted, error in
            print("Permission granted: \(granted)")
        }
    }
}

// AppDelegate
func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    sendTokenToServer(token)
}

func application(
    _ application: UIApplication,
    didFailToRegisterForRemoteNotificationsWithError error: Error
) {
    print("Failed to register: \(error)")
}
来自WWDC20-10095:
swift
import UserNotifications

class NotificationManager: NSObject, UNUserNotificationCenterDelegate {

    func setup() {
        UNUserNotificationCenter.current().delegate = self
        UIApplication.shared.registerForRemoteNotifications()
    }

    func requestPermission() {
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert, .sound, .badge]
        ) { granted, error in
            print("权限已授予:\(granted)")
        }
    }
}

// AppDelegate
func application(
    _ application: UIApplication,
    didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
    let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
    sendTokenToServer(token)
}

func application(
    _ application: UIApplication,
    didFailToRegisterForRemoteNotificationsWithError error: Error
) {
    print("注册失败:\(error)")
}

Background Push Notifications

后台推送通知

swift
// Handle background notification
func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    // Check for content-available flag
    guard let aps = userInfo["aps"] as? [String: Any],
          aps["content-available"] as? Int == 1 else {
        completionHandler(.noData)
        return
    }

    Task {
        do {
            let hasNewData = try await fetchLatestContent()
            completionHandler(hasNewData ? .newData : .noData)
        } catch {
            completionHandler(.failed)
        }
    }
}
swift
// 处理后台通知
func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    // 检查content-available标志
    guard let aps = userInfo["aps"] as? [String: Any],
          aps["content-available"] as? Int == 1 else {
        completionHandler(.noData)
        return
    }

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

Server Payload Examples

服务器负载示例

json
// Alert notification (user-visible)
{
    "aps": {
        "alert": {
            "title": "New Message",
            "body": "You have a new message from John"
        },
        "sound": "default",
        "badge": 1
    },
    "message_id": "12345"
}

// Background notification (silent)
{
    "aps": {
        "content-available": 1
    },
    "update_type": "new_content"
}
json
// 提醒通知(对用户可见)
{
    "aps": {
        "alert": {
            "title": "新消息",
            "body": "你收到了来自John的新消息"
        },
        "sound": "default",
        "badge": 1
    },
    "message_id": "12345"
}

// 后台通知(静默)
{
    "aps": {
        "content-available": 1
    },
    "update_type": "new_content"
}

Push Priority Headers

推送优先级头部

PriorityHeaderUse Case
High (10)
apns-priority: 10
Time-sensitive alerts
Low (5)
apns-priority: 5
Deferrable updates
Energy tip: Use priority 5 for all non-urgent notifications. System batches low-priority pushes for energy efficiency.

优先级头部使用场景
高(10)
apns-priority: 10
时间敏感的提醒
低(5)
apns-priority: 5
可延迟的更新
节能提示:所有非紧急通知使用优先级5。系统会批量处理低优先级推送以提升能源效率。

Troubleshooting Checklist

故障排查清单

Issue: App at Top of Battery Settings

问题:应用在电池设置中排名靠前

  • Run Power Profiler to identify dominant subsystem
  • Check for timers without tolerance
  • Check for polling patterns
  • Check for continuous location
  • Check for background audio session
  • Verify BGTasks complete promptly
  • 运行Power Profiler识别主要消耗子系统
  • 检查无容差的计时器
  • 检查轮询模式
  • 检查持续的位置更新
  • 检查后台音频会话
  • 验证BGTasks是否及时完成

Issue: Device Gets Hot

问题:设备发热

  • Check GPU Power Impact for sustained high values
  • Look for continuous animations
  • Check for blur effects over dynamic content
  • Verify Metal frame limiting
  • Check CPU for tight loops
  • 检查GPU功耗影响是否持续偏高
  • 查找持续运行的动画
  • 检查动态内容上的模糊效果
  • 验证Metal帧率限制
  • 检查CPU是否存在死循环

Issue: Background Battery Drain

问题:后台电池消耗

  • Audit background modes in Info.plist
  • Verify audio session deactivated when not playing
  • Check location accuracy and stop calls
  • Verify beginBackgroundTask calls end promptly
  • Review BGTask scheduling
  • 审核Info.plist中的后台模式
  • 验证音频会话在不播放时是否已停用
  • 检查位置精度和停止调用
  • 验证beginBackgroundTask是否及时调用end
  • 审查BGTask调度

Issue: High Cellular Usage

问题:蜂窝网络使用过高

  • Check allowsExpensiveNetworkAccess setting
  • Verify discretionary flag on background downloads
  • Look for polling patterns
  • Check for large automatic downloads

  • 检查allowsExpensiveNetworkAccess设置
  • 验证后台下载的discretionary标志
  • 查找轮询模式
  • 检查大型自动下载

Expert Review Checklist

专家评审清单

Timers (10 items)

计时器(10项)

  • Tolerance ≥10% on all timers
  • Timers invalidated in deinit
  • No timers running when app backgrounded
  • Using Combine Timer where possible
  • No sub-second intervals without justification
  • Event-driven alternatives considered
  • No synchronization via timer polling
  • Timer invalidated before creating new one
  • Repeating timers have clear stop condition
  • Background timer usage justified
  • 所有计时器容差≥10%
  • 计时器在deinit中失效
  • 应用后台时无计时器运行
  • 尽可能使用Combine Timer
  • 无正当理由不使用亚秒级间隔
  • 考虑过事件驱动替代方案
  • 无通过计时器轮询进行同步的情况
  • 创建新计时器前先失效旧计时器
  • 重复计时器有明确的停止条件
  • 后台计时器使用有合理理由

Network (10 items)

网络(10项)

  • waitsForConnectivity = true
  • allowsExpensiveNetworkAccess appropriate
  • allowsConstrainedNetworkAccess appropriate
  • Non-urgent downloads use discretionary
  • Push notifications instead of polling
  • Requests batched where possible
  • Payloads compressed
  • Background URLSession for large transfers
  • Retry logic has exponential backoff
  • Connection reuse via single URLSession
  • waitsForConnectivity = true
  • allowsExpensiveNetworkAccess设置合理
  • allowsConstrainedNetworkAccess设置合理
  • 非紧急下载使用可自主调度模式
  • 使用推送通知而非轮询
  • 请求已尽可能批量处理
  • 负载已压缩
  • 大型传输使用后台URLSession
  • 重试逻辑具备指数退避
  • 通过单个URLSession复用连接

Location (10 items)

位置(10项)

  • Accuracy appropriate for use case
  • distanceFilter set
  • Updates stopped when not needed
  • pausesLocationUpdatesAutomatically = true
  • Background location only if essential
  • Significant-change for background
  • CLMonitor for region monitoring
  • Location permission matches actual need
  • Stationary detection utilized
  • Location icon explained to users
  • 精度符合使用场景需求
  • 已设置distanceFilter
  • 不需要时已停止更新
  • pausesLocationUpdatesAutomatically = true
  • 仅在必要时使用后台位置
  • 后台使用显著位置变化监控
  • 使用CLMonitor进行区域监控
  • 位置权限与实际需求匹配
  • 已利用静止检测
  • 已向用户说明位置图标含义

Background Execution (10 items)

后台执行(10项)

  • endBackgroundTask called promptly
  • Expiration handlers implemented
  • BGTasks use requiresExternalPower when possible
  • EMRCA principles followed
  • Background modes limited to needed
  • Audio session deactivated when idle
  • Progress saved incrementally
  • Tasks complete within time limits
  • Low Power Mode checked before heavy work
  • Thermal state monitored
  • endBackgroundTask及时调用
  • 已实现过期处理程序
  • 可能时BGTasks使用requiresExternalPower
  • 遵循EMRCA原则
  • 后台模式仅限于必要的使用
  • 音频会话在闲置时已停用
  • 已逐步保存进度
  • 任务在时间限制内完成
  • 执行繁重工作前检查低电量模式
  • 已监控热状态

Display/GPU (10 items)

显示屏/GPU(10项)

  • Dark Mode supported
  • Animations stop when view hidden
  • Frame rates appropriate for content
  • Secondary animations lower priority
  • Blur effects minimized
  • Metal has frame limiting
  • Brightness-independent design
  • No hidden animations consuming power
  • GPU-intensive work has visibility checks
  • ProMotion considered in frame rate decisions

  • 支持深色模式
  • 视图不可见时停止动画
  • 帧率与内容需求匹配
  • 次要动画优先级较低
  • 模糊效果已最小化
  • Metal已设置帧率限制
  • 设计与亮度无关
  • 无隐藏动画消耗电力
  • GPU密集型工作有可见性检查
  • 帧率决策已考虑ProMotion

WWDC Session Reference

WWDC会话参考

SessionYearTopic
2262025Power Profiler workflow, on-device tracing
2272025BGContinuedProcessingTask, EMRCA principles
100832022Dark Mode, frame rates, deferral
100952020Push notifications primer
7072019Background execution advances
4172019Battery life, MetricKit

Last Updated: 2025-12-26 Platforms: iOS 26+, iPadOS 26+
会话编号年份主题
2262025Power Profiler工作流、设备端追踪
2272025BGContinuedProcessingTask、EMRCA原则
100832022深色模式、帧率、延迟处理
100952020推送通知入门
7072019后台执行进阶
4172019电池续航、MetricKit

最后更新:2025-12-26 支持平台:iOS 26+, iPadOS 26+