Streaming is available in most browsers,
and in the Developer app.
-
Power down: Improve battery consumption
Discover how you can limit your power usage and help people get even more out of your app. We'll show you how you can reduce battery drain from your app by making four key changes to your code. Learn how to add Dark Mode to your app and benefit from OLED displays, audit frame rates from secondary animations, limit background data processing, and defer long running tasks.
Resources
Related Videos
WWDC22
WWDC21
WWDC19
-
Download
♪ ♪ Vaibhav Gautam: Hi, I am Vaibhav Gautam, and I am an engineer on the Software Power team. Apps enrich people's lives, as they provide various critical functionalities throughout a day of use. But this use can come with a cost: battery drain. Therefore, it is important that you take special attention to improve your app's battery life so that your users can use their devices and your apps longer. We deeply study different system components to understand power consumption, and in this session, I will go over four key actions we have identified that you can take to significantly improve your app's battery life. These are Dark Mode in your app, auditing frame rates, limiting background time,, and deferring work in your app.
First, I'll talk about Dark Mode.
Dark Mode was introduced in iOS 13, and allows someone to configure their devices in a darker presentation. You may be familiar with the personalization benefits of Dark Mode, but it can also dramatically affect battery life.
This is because on devices with OLED displays, like iPhone 13 and 13 Pro, darker content consumes less power than lighter content. On an OLED screen each pixel requires individual power, and for darker colors less power is required to light up the pixels. Among all the components in the system, display is one of the major sources of power consumption. In fact, in typical use cases, the display can be the leading contributor to battery drain. You have a way to influence the power consumption of the display. And one way is by adopting Dark Mode.
I'll use the Food Truck app my team has been working on as an example. This app has a very prominent background color that takes up most of the screen. When presenting in Dark Mode, this background color becomes much darker than its Light Mode version, contributing heavily to battery savings. In fact, for cases like this, we expect up to a 70% display power savings as a result. This is a huge savings! And when screen brightness is high, the battery savings are even higher. For users who prefer Dark Mode, this is a tremendous opportunity to save them some battery drain and it can reduce thermal load too.
To adopt Dark Mode, start by reviewing how your app currently appears when Dark Mode is enabled. Figure out what components of your app you should update to fit in better with system UI. Xcode makes this easy by using the appearance feature when building your app.
Your app may have hard coded colors because it only supports Light Mode. Use dynamic colors in Xcode to support background color, images, and text in Light and Dark Mode. The system will automatically use the correct color values, and will update when the mode changes.
Your app should also support alternate images for Light Mode and Dark Mode. To learn more about customizing your app for Dark Mode, check out "Implementing Dark Mode on iOS" from WWDC 2019.
Now that you know how to adopt Dark Mode in your app, you should also think about how to adopt Dark Mode for your web content. Safari does not auto-darken any web content, so make sure you adopt Dark Mode for your web content too.
To do so, implement the color-scheme property in your website's stylesheet.
This enables the default text and background colors of the web page to match the current system appearance, standard form controls, and scrollbars. Other named system colors change their look, switching between Light and Dark Mode. Start using stylesheet variables wherever colors are referenced in your stylesheet. This allows your web content to update its colors as the device switches between Light and Dark. Apply the same logic to images and other media assets on your web pages, with different variants for different modes. To learn more about implementing Dark Mode for web content, refer to "Supporting Dark Mode in Your Web Content" from WWDC 2019. Another way to reduce your app's power usage is to audit your frame rates. On devices with ProMotion displays, the refresh rate can affect power consumption. Higher refresh rates use higher power. The frame rates of animations in your app determine the display's refresh rate. Be considerate of the primary content in your app and what frame rate it requires, as not all content in your app may require a high frame rate. The display's refresh rate is determined by the animation with the highest frame rate in your app. Your app may have secondary elements refreshing at a higher rate than necessary, causing the app as a whole to consume more battery than expected.
Here we have the food truck app again. The primary truck scene at the top is rendering at 30 frames per second. Below the truck, there is a text overlay "Food Truck," which is horizontally scrolling. This secondary text is rendering at 60 frames per second. As a result, the whole screen is now rendering at 60 frames per second. If we change the text animation to 30fps, then the entire screen can render at 30fps, and we could save up to 20% of the battery drain. Amazing! To debug and get more information about the frame rates in your app, use Instruments. Use the CoreAnimation FPS instrument to see a timeline showing the frame rate of your app over time. Start by auditing main user scenarios. To identify if frames are being rendered at the rate you expect, determine if secondary elements on the screen have a higher frame rate than your primary content.
Your app may be using CADisplayLink offered by CoreAnimation on iOS to drive custom animations and custom render loop. CADisplayLink is a timer synchronized with the display refresh rate. It provides necessary timing information to your app so that your custom drawing can be aware of refresh events. Your app can give hints to a CADisplayLink object about the desired screen refresh rate. Set the preferredFrameRateRange of a CADisplayLink and specify your minimum, maximum, and preferred frame rates.
Display link will then choose the nearest available frame rate to your preferred rate, based on what the system can handle. If it cannot provide that rate, it will try to stay within your specified range. To configure your display link, initialize it with a target and selector. The provided selector is used to perform custom animation and calculate which video frame to display next. Once your display link is initialized, set the preferred frame rate range. In this example, the preferred rate is 30, but the range can handle anything between 10 and 60. Finally, add the display link to the current run loop.
Keep refresh rates in mind when thinking about your app's battery consumption. This is especially important for devices with ProMotion displays that support very dynamic refresh rates. Monitor your app's frame rates with Instruments to discover issues before you release your app. Lastly, provide information to the system, using CADisplayLink to limit refresh rate for your app's content.
To learn more about frame rate optimizations, refer to "Optimize for variable refresh rate displays" from WWDC 2021. Now, let's talk about how you can power down your app when it's running in the background. When someone switches from your app to a different app, your app may rely on background execution APIs to continue to run in the background. While running in the background, your app may continue to use common services like location and audio. Running these services for long durations will cause battery drain, so when your app is using these services in background, you need to be especially careful! So let's talk about how to avoid excess drain when using these modes Location services keep the device awake to continuously stream location. Even though the app is invisible to the user, it could be continuously streaming location in the background, and cause excess battery drain. It is important to make sure, that you are on top of background location session runtime in your app. When you no longer need the session, make sure your app calls stopUpdatingLocation() to stop the session. During different stages of app development, you can use different tools to find out background location usage, which may not be expected. While building and testing the app, Xcode gauges can be used to find out system energy usage, as well as background location usage. When testing your app before release, you can use MetricKit to collect diagnostic information for a day of use. New in iOS 16 is the location usage in Control Center. Xcode gauges provide information about system usage such as CPU, Network, and Location usage. Xcode gauges will show a timeline of the location usage and energy impact of your app. Looking at this timeline view can be a great way to verify that your location runtime stops when you expect it to stop.
Another tool is to use Metric Kit when testing your apps. Use the cumulativeBackgroundLocationTime property to find out how long your app was actively using location services in background.
New in iOS 16, users can monitor apps which are currently using location services by navigating to Control Center. They can tap the text at the top for a detailed view of apps using location. Use this to get an away-from-your-desk insight into your location runtime. If you see your app here and you don't expect to, it is an indicator that your app has an active location streaming session. We can apply the same principles to audio sessions. Let's say we have a music app that is using the audio player for playing back some file, and the user stops playback.
The app should not only pause or stop the sound, but it should also pause or stop the Audio Engine in order to prevent it from running idle. We recommend using auto shutdown mode which can be enabled by setting the autoShutdownEnabled property of AVAudioEngine class. In this mode, the audio engine continuously monitors and detects if it is idle for a certain duration. When idle, the engine will shut down the audio hardware. And later on, if any of the sources become active again, it will start the audio hardware dynamically. And all of this happens under the hood. On watchOS auto shutdown mode is the enforced behavior. Make sure to stop the Audio Engine when not in use in order to conserve power. The key to limiting background runtime is to remember to tell the system when you are done. The last action you can take to improve battery life is to defer work. Over the course of the day, your app may process lots of different tasks and data. Some of this work needs to occur immediately to service user actions, like rendering content on the screen or playing audio or a video the user taps on.
Other work like machine learning tasks, uploading analytics, or backups is not as time-sensitive. If we defer this time insensitive work to a better time– when the device is charging– we can save battery and avoid contending with the user initiated and interactive work. Let's talk about three APIs you can use to accomplish this. BGProcessingTask is a good choice to defer tasks which run for long durations. Discretionary URLSession is the perfect choice to schedule deferrable networking. And leveraging the right push priority can help servers deliver pushes at the right time. Let's go into detail for each. First is BGProcessingTask. BGProcessingTask lets you defer long-running processing tasks to a better time, such as when the device is charging. It's great for tasks like database cleanup, creating backups, and running machine learning training. To use it, you simply create a request by using BGProcessingTaskRequest API, and provide an application identifier. Then provide more information, such as if your task needs external power or network. Providing more information will help the system schedule the task at a better time window. The system will then launch your app in the background at an opportune time and grant several minutes of runtime to complete the deferrable work. Next is discretionary URLSession. Your app may already be using Background URLSessions for general networking. Background URLSessions get even better when you use the discretionary flag. URLSessions with discretionary flag are networking transactions offloaded to the system to perform networking at a more optimal time, such as when the device is plugged in and connected to Wi-Fi. The discretionary flag is great for non-user initiated long-running networking, such as telemetry collection or downloading the next episode of a TV show. And because the networking was offloaded, it means your app does not need to be running while the network transaction completes.
To use discretionary URL sessions, you simply set up a background URL session and set isDiscretionary to true. You can provide additional information to help the system schedule the download at the right time. Pass in timeout intervals so that the system does not attempt to download forever, causing battery drain.
If you don't want to upload or download data until some point later in the future, pass in an earliest begin date. Finally, pass in an expected workload size so that the system can intelligently load balance between your various download tasks.
Similar to how you can control the immediacy of certain operations with BGProcessingTask and discretionary URL sessions, you can influence the immediacy of push delivery by using different push priorities. Push priority determines how urgently a push needs to be delivered to the device. For a high priority push, the server will send the push immediately to the device, potentially waking the device and causing battery drain. For low priority pushes, the server will delay sending the push until an opportune time, such as when the device is awake or a high priority push comes through. High priority pushes are great for urgent messages like a severe weather warning. Low priority pushes are great for more passive notifications that aren't urgent and can be deferred. Leveraging low priority push to delay the delivery of deferrable messages will save battery as the device will not have to wake as frequently from sleep. To configure low priority pushes, simply set apns-priority to 5 in the push payload. The server will take care of the rest, and your users will appreciate the battery life savings.
So let's wrap it up with some final thoughts and next steps. Provide an option of Dark Mode in your app. If a user chooses Dark Mode, respecting their intent can save battery. Review your animations and hunt for opportunities to reduce frame rates to what's necessary. One small animation can have a big impact. Keep a close eye on your background runtime by letting the system know when you are done. Finally, consider deferring long-running background work to a better time, such as when the device is connected to a charger. If you do all this, then you will have truly powered down your app. Thank you so much.
-
-
8:02 - Create a CADisplayLink
// Create a display link func createDisplayLink() { let displayLink = CADisplayLink(target: self, selector: #selector(step)) // Configure your desired refresh rate by calling preferredFrameRateRange displayLink.preferredFrameRateRange = CAFrameRateRange(minimum: 10, maximum: 60, preferred: 30) // then activate your CADisplayLink by adding it to the main runloop. displayLink.add(to: .current, forMode: .defaultRunLoopMode) }
-
16:03 - Discretionary URLSession
// Set up background URL session let config = URLSessionConfiguration.background(withIdentifier: "com.app.attachments") let session = URLSession(configuration: config, delegate: ..., delegateQueue: ...) // Set discretionary config.isDiscretionary = true // Set timeout intervals config.timeoutIntervalForResource = 24 * 60 * 60 config.timeoutIntervalForRequest = 60 // Create request and task var request = URLRequest(url: url) request.addValue("...", forHTTPHeaderField: "...") let task = session.downloadTask(with: request) // Set time window of two hours task.earliestBeginDate = Date(timeIntervalSinceNow: 2 * 60 * 60) // Set workload size task.countOfBytesClientExpectsToSend = 160 task.countOfBytesClientExpectsToReceive = 4096 task.resume()
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.