Search code examples
iosobjective-caudiobackgroundavaudioplayer

Audio playing while app in background iOS


I have an app mostly based around Core Bluetooth. When something specific happens, the app is woken up using Core Bluetooth background modes and it fires off an alarm, however I can't get the alarm working when the app is not in the foreground.

I have an Alarm Singleton class which initialises AVAudioPlayer like this:

NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]
                    pathForResource:soundName
                             ofType:@"caf"]];

self.player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
[self.player prepareToPlay];
self.player.numberOfLoops = -1;
[self.player setVolume:1.0];

NSLog(@"%@", self.player);

This is the method that is called when my alarm code is called:

-(void)startAlert
{
    NSLog(@"%s", __FUNCTION__);

    playing = YES;
    [self.player play];
    NSLog(@"%i", self.player.playing);

    if (vibrate) {
        [self vibratePattern];
    }
}

Now when the app is in the foreground, self.player.playing returns 1 however when the app is in the background self.player.playing returns 0. Why would this be? All the code is being called, so the app is awake and functioning. The vibrate works perfectly which uses AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);

Any idea why this sound won't play?

Thanks


Solution

  • I have an App than also needs background audio but my App works with the App background mode "Voice over IP" as it needs to record sometimes. I play background audio telling the App singleton I need to play audio in background:

    UIBackgroundTaskIdentifier newTaskId = UIBackgroundTaskInvalid;
    if([thePlayer play]){
        newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];   
    }
    

    EDIT: You must call [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL]; before your app goes to background. In my app, it is at the same time you start playing, in yours, if the player might be started in background, you should do:

    - (void)applicationDidEnterBackground:(UIApplication *)application{
      // You should retain newTaskId to check for background tasks and finish them 
      newTaskId = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];   
    }