Category: App Development


Polling Battery percentage on iPhone


Introduction:

This article explores techniques on how to periodically poll/check battery percentage on an Apple iPhone device using X Code and objective-c.

Use case/Purpose:

I have three apple devices ( two iPhones and a MacBook Air ). Without having to physically locate each device, I want to be able to check the battery percentage of each of the devices.

Covered in this post:

This post covers the mechanics of how to poll for battery percentage on an iOS device, polling for battery percentage on a MacOs device is covered in another article:

https://nsaxelby.com/2018/05/10/polling-battery-percentage-on-macos/

Implementation (iOS):

Theory:

We want a background task to periodically check the battery percentage and do something with it such as post it to an external service ( external service not included in this post ). We want to know the status of the battery every 5 minutes which checks the battery level.

We should do this by using a NSTimer and using UIDevice to query the batteryLevel.

Step 1: Setting up a poller to output the battery percentage level to output

We can set this up by initialising the NSTimer  from the didFinishLaunchingWithOtions  method of the AppDelegate.m  file.

If you run the application now, you will see the following in the output:

Note: if you are running the simulator ( as opposed to a real device ) the percentage will show as -100 because the simulator does not have a battery.

This is a good start, however, if the user minimises the application to the background ( press the home button on the device so that the app is not longer the ‘active’ app ), the NSTimer  will stop. The use-case of this application was to be able to monitor the battery percentage of the device when the user does not have the device to hand. For this scenario, it is therefore necessary to allow the NSTimer  to continue to poll the battery percentage even whilst the app is in the background.

Note: if you are running the simulator, you will see that the NSTimer continues polling in the background even when the app is in the background. The simulator works differently from the real device in this case.

This is where we hit a problem. After doing some research, I found Apple does not want an app to process in the background unless there is a very good reason for the app to do so. The full explanation on background execution modes is available here:

https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/BackgroundExecution/BackgroundExecution.html

To summarise the page, apps that are allowed to periodically execute within the background:

  • GPS type applications ( navigation apps )
  • Music/VOIP apps ( music streaming, or communication apps such as Skype )
  • Apps which talk to bluetooth devices ( headphones, etc )

There are other types of apps, but the frequency/control of the background execution of the other types in the list, are not suitable for this use case ( we want polling every 5 minutes ).

What does this mean? Basically, if you are planning on releasing a pure battery monitoring app ( pure as in; no other major functionality ) to the app store, you are likely to be unsuccessful with your submission.

In my case, I was not planning on releasing the app to the app store, so I had to find a technical workaround ( aka a hack ) in order for my NSTimer  to be able to execute even when in the background.

Step 2: Find a workaround to allow background execution

Theory: Alter the app so that it has a background execution attribute, and then imitate that background execution mode to keep the app alive in the background.

Firstly we need to allow the background execution mode within the capabilities of the app, we would state within the build settings that this app is of a type of ‘Audio, AirPlay and Picture in Picture’ app. We enable Background Modes capabilities within the Targets of the app:

If we run the application now, you will see that it has made no difference, when the app resides to the background, the NSTimer  stops working.

In order to keep the app alive in the background, we need to act like a regular audio player app. To do this, we will add an  AVPlayer to the code, and have that player play a silent track on a continuous loop.

Within the AppDelegate.h import the AVFoundation library:

Add a AVPlayer as a property:

Now within the AppDelegate.m  it’s time to setup the AVPlayer  to begin playing a silent track upon app startup.

Put the following code into the didFinishLaunchingWithOptions  method:

 

You will notice that we referenced a resource with the name "2-seconds-of-silence"  which has an extension of ".mp3" , this is the silent audio track that we need to go and obtain and import into the project before running the app.

Another key mention is the use of the withOptions:AVAudioSessionCategoryOptionMixWithOthers option that is used within the AVVideoSession . This option allows the silent track to continue playing and not affect other audio apps, the silent track will just mix into whatever other audio is playing, and given that the track we are playing is silent, it should have no affect on other audio output.

The complete didFinishLaunchingWithOptions will now look something like this:

Step 3: Obtain the silent audio track

I obtained my silent audio track from the follow GitHub repo:

https://github.com/anars/blank-audio

You can use pretty much any duration of silent audio track, it should not make a difference which one you choose, I chose the 2 second audio track.

Once downloaded, you simply have to drag and drop it into your project from your finder window, and drop it into the list of files within your project navigator:

In order to test the background/foreground execution, I put an additional couple of logs within AppDelegate.m into the background/foreground event handlers, so that I could check that execution was happening where it should do:

If we execute our application on a real device, and put the app into the background ( press home button ) it should now give output like this:

Full git source for the project available at GitHub:

https://github.com/nsaxelby/BatteryMonitorApp

MacOs application is explained in another post within my blog:

https://nsaxelby.com/2018/05/10/polling-battery-percentage-on-macos/

Out of interest, this app allowed me to build up a history of how my iPhone battery performs over time, example screenshot of data that I was able to obtain, ( I may post the source code for the full project at a later date ).