Building an energy-efficient analytics SDK for iOS

By Stephen Panaro, Software Engineer

The primary goal of any analytics SDK is to gather accurate data. Answers goes a step further by putting an equal focus on timeliness. Correct analytics are one thing, but tracking them in real-time is a super power. Is your latest release stable and well-adopted? Is your 24-hour sale driving more purchases? We’ll give you the answer immediately so you can take appropriate action. To deliver this real-time insight, we have to pay special attention to how we design Answers for iOS, tvOS, and macOS.

Low power as a feature

We could have built Answers to make a network request for every app event as it happens. This would have been the most timely SDK, but it would have also drained battery life. Or, we could have designed Answers to collect large batches of events and sacrifice all timeliness for great battery life. Instead, we took a balanced approach in between these two extremes. It has served Answers well, but we wanted to revisit our implementation to see how much better we could make it.

With Answers 1.3 for Apple platforms, we introduced several new power optimizations throughout the SDK. We prioritized application performance and user battery life while keeping latency as low as we could. In some cases, these optimizations produced significant power gains. This allowed us to have a large impact on battery life with a negligible aggregate impact on Answers’ latency. In the next section, we’ll walk through some of the improvements we made to enable these gains.

Limit networking

First, we turned our focus to Answers’ networking. Networking can have a substantial power impact, which made it a great place to look for optimizations. A simple way to reduce its impact is by limiting the number of requests you make. Answers has always done this by sending data in batches. For our latest release, we try even harder to ensure that app events that occur close in time to each other are sent in the same batch. This is most beneficial for heavy users of Answers Events, especially when events are logged in bursts. We also tuned our retry policy to help prevent us from making requests that are unlikely to succeed.

Background uploads

It’s impossible to talk about power-efficient networking without mentioning NSURLSession’s background uploading capability. We’ve used this capability for several years in crash reporting and seen really good power and reliability wins, so we wanted to bring those to Answers. Unfortunately, we found some issues with the background uploading API in iOS 10. Because of that, it is currently only suitable for extremely low volume networking. We hope to enable it in a future release when these issues have been addressed.

Low power mode

Next, we took advantage of NSProcessInfo’s lowPowerModeEnabled property, which notifies apps when a device enters low power mode. Introduced in iOS 9, this is a very strong indication that the user wants battery life to be maximized. When devices are in low power mode, Answers retries network requests less frequently. On macOS, we take similar action when thermal conditions are elevated. This is an easy way to further reduce our power impact, and is particularly effective when networking conditions are poor. We also have plans to expand our adoption of low power mode to other parts of the SDK.

Quality of service

In addition to the low power mode API, we also adopted two easy to use APIs to better inform the OS of the priority of Answers’ work. First, we made sure to set the qualityOfService property of our NSOperationQueues. This was a one-line change and makes sure that Answers always defers to your app’s needs. We also made extensive use of NSProcessInfo’s activity APIs to help the OS understand what we’re doing and how our work should be prioritized. As an added benefit, this makes it more obvious what Answers’ threads are doing if you happen to catch them in a debugger.

Optimized timers

Finally, we wanted to improve our usage of timers. Answers has always relied on timers because we have to make sure we periodically relay any events back to our system. In this release, we updated our timers to fire less frequently. We also now choose a timer’s tolerance value based on its duration to help the OS schedule work more efficiently. On macOS, we’ve adopted NSBackgroundActivityScheduler. This is similar to a timer but takes into account even more system conditions when scheduling work. We discovered this API while reading Apple’s Energy Efficiency guidelines, which has many other useful tips.

Same Answers, more battery

When we started to plan for Answers 1.3, we knew improving energy efficiency and satisfying Answers’ design goals would be challenging. Being accurate and being real-time are inherently power-hungry, but they’re essential to Answers so we were determined to find a solution.

We stepped back, took a holistic look at Answers and identified our best opportunities for improvement. Fortunately, there was a large overlap between the problems we wanted to solve and the problems that Apple supplies a solution for as a part of iOS. This was fantastic on two fronts: it let us write less code and in many cases, it unlocked optimizations that wouldn’t be possible otherwise.

We’re thrilled with how this approach turned out and are even more excited to share these changes with you in the latest Answers release. We hope that this makes it easier for you to build the best apps with the lowest impact on your users’ battery.

If you’re interested in further reducing your app’s impact on battery life, we encourage you to incorporate these best practices into your development workflow. Most are simple to learn and implement and the more apps that adopt them, the greater impact they will have. Plus, no one wants their app to be at the top of the battery section as the worst offender in Settings!