15

There are a lot of questions relating to background music playback in iOS on StackOverflow. None fully explore all edge cases, the aim of this question is to be the final word in background audio question on iOS

Definitions & Assumptions

All the code, questions and examples refer to .

"background" — The state an app is put into when the user presses the home button or the power button (so the devices displays the lock screen). The app can also be put into background using the multitasking switcher or the multitasking gestures on iPad.

"audio" — Audio played back using AudioQueue (including AVAudioPlayer)

Prerequisites

As I understand it there are 2 requirements to get an app to play audio in the background.

  1. Set UIBackgroundModes to audio in the Info.plist
  2. [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

Requirements

My use-case is playing relatively long audio in the background (music). There are potentially hundreds of tracks and the app will play them sequentially. It can be considered that the audio will play indefinitely.

The app will handle interruptions by pausing the playback.

Questions

I've had mixed success with:

[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:...];

Allowing audio to play in the background. But I'm confused as to if its required and how it differs to:

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];

Edge Cases

  1. Interruptions. If you register to be notified of audio interruptions (phone calls etc), by becoming the delegate of AVAudioPlayer. For example, if you then pause or stop your audio when an interruption starts and resume when it ends is your app suspended if the interruption exceeds 10 minutes (max time allowed for background tasks to complete)?

  2. The Simulator will stop the audio if Lock or Home are invoked, while using:

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    

However this works on a device. Is this a known issue?

Felix
  • 35,354
  • 13
  • 96
  • 143
Richard Stelling
  • 25,607
  • 27
  • 108
  • 188

2 Answers2

5

I have some experience with GPS background mode, and background audio. This is not exactly the same as your situation (you want to play a long audio file, and I play short messages) but here's what I can tell you:

  • beginBackgroundTaskWithExpirationHandler This selector has one purpose when being invoked when in background: avoid the application to return to the suspended state in which no code can be invoked anymore (you're "frozen"). So as long as you invoked beginBackgroundTaskWithExpirationHandler and before you terminated your long running task with beginBackgroundTaskWithExpirationHandler, you use the CPU, and consume battery.
    I really doubt that playing a file in the background should use the battery of the iPhone as if it was running an app so I doubt that beginBackgroundTaskWithExpirationHandler is really involved in your flow.

  • Simulator: don't rely on the simulator: it does not fully implement background modes. Actually, when you click on the home button, your app goes in background, but at this stage, you may still be able to execute code in your app. After a while, then, your app will be suspended (=frozen), and your code execution will be suspended in order to save the battery. This suspended state will never occur on the simulator.

  • Interruptions. It's not up to you to pause/resume the playback when a phone call comes in. the platform is in charge of this, and you can just react to this with your AVAudioSessionDelegate . However, you can influence the way your session is going to interact with other audio sounds by setting property on your audio session (see kAudioSessionProperty_OverrideCategoryMixWithOthers for instance). So the flow is more: your describe the way your audio session should interact with the rest of the system, the system will mix the sounds accordingly to that, and if your session gets interrupted, you'll be notified with the AVAudioSessionDelegate.

Hope this helps.

yonel
  • 7,855
  • 2
  • 44
  • 51
3

I have used below code to Device Control -

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];

Used to get register for listening the remote control. Once done remove it -

[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
[self resignFirstResponder];

make the App canBecomeFirstResponder-

- (BOOL)canBecomeFirstResponder {
    return YES;
}

Used delegate method to handle iPhone control, like play and pause while doble tap on the home button

- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    //if it is a remote control event handle it correctly
    if (event.type == UIEventTypeRemoteControl) {
        if (event.subtype == UIEventSubtypeRemoteControlPlay) {
           [audioPlayer play];
            NSLog(@"play");
        } else if (event.subtype == UIEventSubtypeRemoteControlPause) {
           [audioPlayer stop];
             NSLog(@"pause");
        } else if (event.subtype == UIEventSubtypeRemoteControlTogglePlayPause) {
            NSLog(@"toggle");
        }
    }
}
Sanoj Kashyap
  • 5,020
  • 4
  • 49
  • 75