Energy Diagnostics
Understanding Energy Impact
iOS monitors your app energy usage and classifies it as Minimal, Low, Medium, or High. The Energy Impact instrument in Instruments shows exactly what drains battery.
Major Energy Consumers
- CPU usage: Sustained high CPU usage is the biggest battery drain
- Network activity: Radio is power-hungry, especially cellular
- Location services: GPS is extremely energy-intensive
- Display: Screen-on time and high frame rates consume power
- Background activity: Tasks running while app is suspended
Measuring Energy Impact
In Xcode, go to Product, Profile, then select Energy Log instrument. It shows:
- CPU usage over time
- Network bytes sent and received
- Location updates frequency
- Background task execution
- Battery level changes
Common Energy Pitfalls
// Bad: Polling with Timer
Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
fetchUpdates() // Wakes CPU every second
}
// Good: Use Push Notifications or Background Tasks
func scheduleRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 3600)
try? BGTaskScheduler.shared.submit(request)
}
Responding to Low Power Mode
NotificationCenter.default.addObserver(
forName: .NSProcessInfoPowerStateDidChange,
object: nil,
queue: .main
) { _ in
if ProcessInfo.processInfo.isLowPowerModeEnabled {
// Reduce animation frame rate
// Stop non-essential background tasks
// Reduce network frequency
} else {
// Resume normal behavior
}
}
Background Tasks
BGTaskScheduler
iOS provides BGTaskScheduler for efficient background work. The system batches tasks to minimize wake-ups:
// Register task handler
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.app.refresh",
using: nil
) { task in
self.handleRefresh(task: task as! BGAppRefreshTask)
}
// Schedule a refresh
func scheduleRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
try? BGTaskScheduler.shared.submit(request)
}
// Handle the task
func handleRefresh(task: BGAppRefreshTask) {
let operation = RefreshOperation()
task.expirationHandler = { operation.cancel() }
operation.completion = { success in
task.setTaskCompleted(success: success)
}
OperationQueue.main.addOperation(operation)
scheduleRefresh() // Schedule next refresh
}
Background App Refresh
Enable Background App Refresh in Settings and use it wisely:
- Update content that changes frequently (news, social feeds)
- Sync data that the user expects to be current
- Pre-fetch content the user will likely need
Processing vs Refresh Tasks
- BGAppRefreshTask: Lightweight updates, limited time
- BGProcessingTask: Heavy work like downloads, sync, analysis. Requires Background Mode capability.
// Processing task for heavy work
let request = BGProcessingTaskRequest(identifier: "com.app.sync")
request.requiresNetworkConnectivity = true
request.requiresExternalPower = false
try? BGTaskScheduler.shared.submit(request)
Avoid Excessive Background Work
iOS may terminate apps that abuse background time. Follow these guidelines:
- Complete background work quickly (typically under 30 seconds)
- Schedule tasks at reasonable intervals
- Respect the system scheduling decisions
- Always schedule the next task at the end of the current one
Network Efficiency
The Cost of Network Requests
Every network request activates the cellular or WiFi radio, which stays active for several seconds after the last byte. Multiple small requests waste energy because the radio cannot power down between them.
Batching Requests
// Bad: Three separate requests
async func loadDashboard() async throws -> Dashboard {
let user = try await api.fetchUser()
let posts = try await api.fetchPosts()
let notifications = try await api.fetchNotifications()
return Dashboard(user: user, posts: posts, notifications: notifications)
}
// Good: Single combined request
async func loadDashboard() async throws -> Dashboard {
return try await api.fetchDashboard() // One request returns all data
}
// Or parallel requests (still better than sequential)
func loadDashboard() async throws -> Dashboard {
async let user = api.fetchUser()
async let posts = api.fetchPosts()
async let notifications = api.fetchNotifications()
return try await Dashboard(
user: user, posts: posts, notifications: notifications
)
}
Caching Strategy
class NetworkCache {
private let cache = NSCache<NSString, CachedResponse>()
private let session: URLSession
func fetch(_ request: URLRequest) async throws -> Data {
let key = request.url!.absoluteString as NSString
if let cached = cache.object(forKey: key),
!cached.isExpired {
return cached.data
}
let (data, _) = try await session.data(for: request)
cache.setObject(
CachedResponse(data: data, expiry: Date().addingTimeInterval(300)),
forKey: key
)
return data
}
}
Cellular vs WiFi
Check network type and adjust behavior:
import Network
class NetworkMonitor {
static let shared = NetworkMonitor()
private let monitor = NWPathMonitor()
private(set) var connectionType: ConnectionType = .unknown
enum ConnectionType { case wifi, cellular, unknown }
func start() {
monitor.pathUpdateHandler = { [weak self] path in
if path.usesInterfaceType(.wifi) {
self?.connectionType = .wifi
} else if path.usesInterfaceType(.cellular) {
self?.connectionType = .cellular
}
}
monitor.start(queue: .global())
}
}
Download high-quality content on WiFi, defer large downloads on cellular, and use compressed formats when on cellular.
Quiz
1. Which is the most energy-intensive operation on iOS?
2. What does BGTaskScheduler provide?
3. Why is batching network requests better for battery?
4. How should you respond to Low Power Mode?
Flashcards
Question
What are the main energy consumers in an iOS app?
Click to reveal answer
Answer
CPU usage, network activity (especially cellular radio), GPS location services, display brightness, and background tasks.
Question
What is the difference between BGAppRefreshTask and BGProcessingTask?
Click to reveal answer
Answer
BGAppRefreshTask is for lightweight quick updates. BGProcessingTask is for heavier work like downloads and sync, requiring Background Mode capability.
Question
How do you check for Low Power Mode in code?
Click to reveal answer
Answer
Use ProcessInfo.processInfo.isLowPowerModeEnabled to read the state, and observe .NSProcessInfoPowerStateDidChange for changes.
Revision Notes
Key Takeaways
- 1. GPS location tracking is the biggest battery drain
- 2. Batch network requests to reduce radio wake-ups
- 3. BGTaskScheduler provides system-managed background execution
- 4. Respond to Low Power Mode by reducing activity
- 5. Always test energy impact with Instruments Energy Log
Interview Tips
- • Discuss strategies for reducing battery consumption
- • Explain how BGTaskScheduler works and its limitations
- • Describe how you would handle Low Power Mode in your app
- • Walk through optimizing a feature for energy efficiency
Cheat Sheet
Battery Optimization Quick Reference
- GPS is the most energy-intensive operation
- Batch network requests to reduce radio wake-ups
- Use BGTaskScheduler for background work
- Respond to Low Power Mode changes
- Defer heavy work to when device is charging
- Cache network responses appropriately