Category: App Development


QuizWithMates.com


Update: 19/09/2021 – Website is now offline.

I built this website due to the recent popularity of online quizzes, or ‘zoom quizzes’. I noticed that the collection of answers/marking of answers ws often a little clunky. One extreme example answer I saw was to send answers to the quizmaster by textmessage/whatsapp! All of which was unnecessary overhead, not to mention quite a lot of work for the host. I saw a need for a smoother way of organizing online quizzes, specifically submitting/collection answers, QuizWithMates.com was born.

More info can be found on the websites about page.

Some Screenshots:

1.Creating a Quiz

2. Quiz Players – Answer question

3. Quiz Host – Realtime Answers

4. Answer Marking/Review

5. Results


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.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
    // For the purposes of testing, I have set the interval to 20 seconds
    [NSTimer scheduledTimerWithTimeInterval:20.0
                                     target:self
                                   selector:@selector(UpdateBatteryStatus)
                                   userInfo:nil
                                    repeats:YES];
    return YES;
}

- (void)UpdateBatteryStatus
{
    UIDevice *myDevice = [UIDevice currentDevice];
    float batteryPercentageLeft = [myDevice batteryLevel] * 100;
    NSLog(@"Battery Percentage: %f", batteryPercentageLeft);
    // TODO add additional code here to do whatever you want with the battery percentage
}

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:

#import <AVFoundation/AVFoundation.h>

Add a AVPlayer as a property:

@property (nonatomic, strong) AVPlayer *player;

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:

 

    // Start playing silent file
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&sessionError];
    
AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:@"2-seconds-of-silence" withExtension:@"mp3"]];
[self setPlayer:[[AVPlayer alloc] initWithPlayerItem:item]];
[[self player] setActionAtItemEnd:AVPlayerActionAtItemEndNone];
[[self player] play];

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:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[UIDevice currentDevice] setBatteryMonitoringEnabled:YES];
    
    // Start playing silent file
    NSError *sessionError = nil;
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&sessionError];
    
    AVPlayerItem *item = [AVPlayerItem playerItemWithURL:[[NSBundle mainBundle] URLForResource:@"2-seconds-of-silence" withExtension:@"mp3"]];
    [self setPlayer:[[AVPlayer alloc] initWithPlayerItem:item]];
    [[self player] setActionAtItemEnd:AVPlayerActionAtItemEndNone];
    [[self player] play];
    
    // For the purposes of testing, I have set the interval to 20 seconds
    [NSTimer scheduledTimerWithTimeInterval:20.0
                                     target:self
                                   selector:@selector(UpdateBatteryStatus)
                                   userInfo:nil
                                    repeats:YES];
    return YES;
}
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:

- (void)applicationDidEnterBackground:(UIApplication *)application {
    NSLog(@"App now in background");
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
        NSLog(@"App now in foreground");
}

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 ).