Introduction
Our application should be energy efficient. It is an unavoidable criteria for users that have bad battery life. If an app consumes more energy, the user may even be ready to delete the application. We know that almost all the processes in the device consume energy. In this article, we will look into some of the energy efficient theories.
Energy Consumption
It is important to understand where more power consumption is happening.
- CPU - Almost all the processes use CPU. So effective utilization of CPU is needed here. It can be achieved only through better optimization of the tasks like batching, prioritizing and scheduling.
- The device is awake – We know that when the device is in sleep mode, it uses less energy. Once it comes to the awake mode, most of its parts start to power up. Make sure that the app lets the device to sleep. Also, don’t wake up the device until it is very much necessary.
- Network operation – When the network layer in your app starts functioning, the app drains more power. Here, we need to make a significant contribution to decrease the power consumption.
- Graphics – Each and every screen update takes extra energy. Standard windows and controls are already designed in a way to use energy efficiently. But when it comes to custom windows and custom controls, make sure that your content is not getting re-rendered until new data is available. Try avoiding UI refresh process with the same content and refreshing the content when its view is not visible.
- Location – If the app keeps tracking the user’s physical location and also tries to access the location data with high accuracy, it leads to high battery usage. So, a smart way of implementation and better planning is required while using location APIs.
Energy Efficient Coding
In this article, we are focusing on the main areas where we can apply optimization for efficient battery usage.
Process
Background Execution
Let us first look at the activities, which can result in unnecessary battery usage:
- Failed to notify the system about background activity completion. It can lead the device to be still active and draw energy
- Playing silent audio
- Updating location when app is in background
- Background downloads or uploads that can be deferred
Here are Some Energy Efficient Coding Practices when App Goes into the Background
- We should allow the App to pause or halt the process, save data or stop any UI updation when App calls
applicationWillResignActive
. This delegate gets called before App moves to an inactive state in case of incoming call, SMS or user switches to another App.
func applicationWillResignActive(_ application: UIApplication) {
}
- If App needs more time to complete the task before being suspended, an API called
beginBackgroundTaskWithExpirationHandler
provides extra time for execution. When the task is completed, we must call endBackgroundTask
: to inform the system that task is completed.
func applicationDidEnterBackground(_ application: UIApplication) {
var bgTask = UIApplication.shared.beginBackgroundTask {
}
DispatchQueue.global().async {
UIApplication.shared.endBackgroundTask(bgTask)
bgTask=UIBackgroundTaskInvalid
}
}
- Background App refresh API is preferable to update your content instead of a timer.
Timer
We need to avoid timer usage as much as possible. It will be a good practice if UI updating can be done on user interaction or new content availability alert. If timer execution is mandatory for your app scenario, then increase the interval.
Prioritize the Work with the Quality of Service (QoS)
If the app can effectively utilize multiple cores, memory, etc. by applying parallelism and concurrency. Then it is not only more responsive, but also utilizes energy efficiently.
When the app performs operations and queues, we can specify the QoS. This will allow the system to adjust the priority of the work for scheduling, CPU and I/O allocation and timer latency.
Following are the primary QoS:
NSQualityOfServiceUserInteractive
NSQualityOfServiceUserInitiated
NSQualityOfServiceUtility
NSQualityOfServiceBackground
Here is some code example of how to create a workItem
with QoS priority.
let userInitiatedWorkItem = DispatchWorkItem(qos: .userInitiated, flags: .assignCurrentContext) {
}
DispatchQueue.main.async(execute: userInitiatedWorkItem)
let bgWorkItem = DispatchWorkItem(qos: .background, flags: .assignCurrentContext) {
}
DispatchQueue.main.async(execute: bgWorkItem)
Network
- Make use of cached data – App should not fetch the same data again and again. It leads to extra cost and extra energy. Download the data when there is a content change or on user interaction.
- Define pausable and resumable network load –
NSURLSession
allows developers to pause or resume transaction in case if it is interrupted. This will help the app to not upload the same content again. - If possible, transfer compressed data.
- If network class keeps trying network load in poor network environment or frequent network disconnect environment or
allowsCellularAccess
set to false
by the user. From iOS 11, we have a new API called waitsForConnectivity
and a brand new delegate called URLSession:taskIsWaitingForConnectivity
. If waitsForConnectivity
is set to true
, and the proper connectivity is unavailable, then the delegate method URLSession:taskIsWaitingForConnectivity
will get called, where we can decide whether to update the UI or show an offline message or to update the UI with cached data and then the session will be waiting for a connection. Once the connection is available, the session will continue to hit the server. waitsForConnectivity
will work only at the first time of connection establishment, once the connection is available and then it drops out during fetching the data, then the completion handler or delegate will notify the failure condition.
let sessionConfig = URLSessionConfiguration.default
sessionConfig.waitsForConnectivity = true
- Try using Defer networking – We can use defer networking concept to carry out less priority network transaction. So the system finds a proper time to execute the task. Maybe while the device is connected to charge or while using the Wi-Fi. The optimized approach is to batch all the less priority transaction and create a background session and try to execute it at a stretch. For deferring the network load, we have to set
NSURLSessionConfiguration.discretionary = true
.
let config = URLSessionConfiguration.background(withIdentifier: "com.example.testApp.background")
config.isDiscretionary = true
Location
- High location accuracy leads to more energy consumption.
- Use
CLLocationManager.requestLocation()
to get current location instead of CLLocationManager.startUpdatingLocation
and CLLocationManager.stopUpdatingLocation
. - Once the user reaches the destination or is done with the task, stop using location service immediately.
- Use
CLCircularRegion
to get the alert when the user enters a particular region instead of keeping the track of location with startUpdatingLocation
and stopUpdatingLocation
.
Analytics
Most of the app integrates Analytics to understand how the users use the App. For the best energy efficient coding practice, we can move the analytics network hit to the background session. It can find the best time for the upload and also the system retries it, in case of failure. To help the system to find the best time to execute your task, we can set new properties start time and work load size.
Conclusion
When the device is idle, less power is utilized. While the application is in running state, the device consumes extra energy than usual. From the user point of view, App is accountable for both the extra energy and the fixed energy consumed by the device. So energy efficient coding is mandatory to provide a great experience for the user. For getting more insight on the above-mentioned points, please have a look at Apple's energy efficient coding practice. In the second part of this article, we will be discussing energy tools in detail.