iOS 7 SDK not abiding background audio iOS 7 SDK not abiding background audio objective-c objective-c

iOS 7 SDK not abiding background audio


I managed to solve this, and to save hair pulling by another poor soul here goes:

Firstly make sure your Info.plist correctly lists audio as a background mode.

(If you dont know what i'm talking about select YOURAPPNAME-Info.plist select that. Click oin the plus sign and add a new key called UIBackgroundModes and expand it. Add a value called audio.)

You'll need a reference to whatever playback object is creating the audio. Since I'm only playing audio and AVplayer was not abiding by the background audio, use this in your view controller's header:

@property (nonatomic, retain) MPMoviePlayerController *audioPlayer;

In the implementation, do the following:

 [super viewDidAppear:animated];    //Once the view has loaded then we can register to begin recieving controls and we can become the first responder    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];    [self becomeFirstResponder];

and

- (void)viewWillDisappear:(BOOL)animated {    [super viewWillDisappear:animated];    //End recieving events    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];    [self resignFirstResponder];

add two methods

//Make sure we can recieve remote control events- (BOOL)canBecomeFirstResponder {    return YES;}- (void) registerForAudioObjectNotifications {    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];    [notificationCenter addObserver: self                           selector: @selector (handlePlaybackStateChanged:)                               name: MixerHostAudioObjectPlaybackStateDidChangeNotification                             object: audioObject];}

now ALL important code - this enables your app to control audio from "control center" and from lock screen:

- (void) remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {    if (receivedEvent.type == UIEventTypeRemoteControl) {        switch (receivedEvent.subtype) {            case UIEventSubtypeRemoteControlTogglePlayPause:                [self playOrStop: nil];                break;            default:                break;        }    }}

you can add many many types of Event types here and call any method.

Typical events are:

 UIEventSubtypeRemoteControlPlay                 = 100,  //Parent EVENT// All below are sub events and you can catch them using switch or If /else.    UIEventSubtypeRemoteControlPause                = 101,    UIEventSubtypeRemoteControlStop                 = 102,    UIEventSubtypeRemoteControlTogglePlayPause      = 103,    UIEventSubtypeRemoteControlNextTrack            = 104,    UIEventSubtypeRemoteControlPreviousTrack        = 105,    UIEventSubtypeRemoteControlBeginSeekingBackward = 106,    UIEventSubtypeRemoteControlEndSeekingBackward   = 107,    UIEventSubtypeRemoteControlBeginSeekingForward  = 108,    UIEventSubtypeRemoteControlEndSeekingForward    = 109,

To Debug help you can use:

 MPMoviePlayerController *mp1= (MPMoviePlayerController *)[notification object];    NSLog(@"Movie State is: %d",[mp1 playbackState]);    switch ([mp1 playbackState]) {        case 0:            NSLog(@"******* video has stopped");            break;        case 1:            NSLog(@"******* video is playing after being paused or moved.");                       break;        case 2:            NSLog(@"******* video is paused");                break;        case 3:            NSLog(@"******* video was interrupted");            break;        case 4:            NSLog(@"******* video is seeking forward");                     break;        case 5:            NSLog(@"******* video is seeking Backwards");            break;        default:            break;

and thats it - hope it helps some one out there! - this is working perfect on iOS 7 and iOS 6 with Storyboard app as well as control using Headphone and all new control centre too.


Apparently the problem was on Apple's side as iOS update 7.0.3 fixes this issue. Besides what Alex noted about UIEventSubtype changes the code that worked on iOS6 now works on iOS7.

For sake of completeness, here is my relevant code that is working in both iOS6 and iOS7 - after the udpate to 7.0.3. Also included AVFoundation.framework and MediaPlayer.framework in project Build Phases -> Link binary with libraries. No code for this in app delegate.

In viewcontroller .h file:

#import <AVFoundation/AVFoundation.h>#import <MediaPlayer/MediaPlayer.h>@interface NewsDetailViewController : UIViewController <UIWebViewDelegate, AVAudioSessionDelegate>@property (nonatomic) MPMoviePlayerController *audioPlayer;

In viewcontroller .m file:

- (void)viewDidLoad{    [super viewDidLoad];    self.audioPlayer = [[MPMoviePlayerController alloc] initWithContentURL:audioUrl];    [self.audioPlayer prepareToPlay];    [self.audioPlayer.view setFrame:CGRectMake(0, 0, self.audioView.frame.size.width,     42)];    self.audioPlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth;    [self.audioView addSubview:self.audioPlayer.view];    [self.audioPlayer play];    NSError *setCategoryError = nil;    NSError *activationError = nil;    [[AVAudioSession sharedInstance] setActive:YES error:&activationError];    [[AVAudioSession sharedInstance] setDelegate:self];    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];}- (BOOL)canBecomeFirstResponder {    return YES;}- (void)viewDidAppear:(BOOL)animated {    [super viewDidAppear:animated];    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];    [self becomeFirstResponder];}- (void)viewWillDisappear:(BOOL)animated {    [self.audioPlayer stop];    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];    [self resignFirstResponder];    [super viewWillDisappear:animated];}- (void)remoteControlReceivedWithEvent:(UIEvent *)receivedEvent {    if (receivedEvent.type == UIEventTypeRemoteControl) {        switch (receivedEvent.subtype) {            case UIEventSubtypeRemoteControlPlay:                [self.audioPlayer play];                break;            case UIEventSubtypeRemoteControlPause:                [self.audioPlayer pause];                break;            case UIEventSubtypeRemoteControlTogglePlayPause:                if (self.audioPlayer.playbackState == MPMoviePlaybackStatePlaying) {                    [self.audioPlayer pause];                }                else {                    [self.audioPlayer play];                }                break;            default:                break;        }    }}


If you want to play audio in background in iphone and simulator also then you need to write this code in plist and Firstly make sure your Info.plist correctly lists audio as a background mode.

(If you dont know what i'm talking about select YOURAPPNAME-Info.plist select that. Click on the plus sign and type a key UIBackgroundModes and enter. Add a value called "App plays audio"(for simulator) or "App plays audio or streams audio/video using AirPlay"(For iphone).)

enter image description here

in AppDelegate.m

- (void)applicationDidEnterBackground:(UIApplication *)application{    __block UIBackgroundTaskIdentifier task = 0;    task=[application beginBackgroundTaskWithExpirationHandler:^{    NSLog(@"Expiration handler called %f",[application backgroundTimeRemaining]);    [application endBackgroundTask:task];    task=UIBackgroundTaskInvalid;    }];}

Add these two framework in your project and some line of code in ViewController.h

#import <AVFoundation/AVFoundation.h>#import <MediaPlayer/MediaPlayer.h>@interface ViewController : UIViewController <UIWebViewDelegate, AVAudioSessionDelegate>@property (nonatomic) MPMoviePlayerController *audioPlayer;

Remind that these frameworks refrences should be added in your project.

Then in Viewcontrller.m

- (void)viewDidAppear:(BOOL)animated {    [super viewDidAppear:animated];    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];    [self becomeFirstResponder];}- (void)viewWillDisappear:(BOOL)animated {    [self.audioPlayer stop];    [[UIApplication sharedApplication] endReceivingRemoteControlEvents];    [self resignFirstResponder];    [super viewWillDisappear:animated];}- (void)viewDidLoad{    [super viewDidLoad];    // Do any additional setup after loading the view, typically from a nib.    NSURL *audioUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"songName" ofType:@"mp3"]];    self.audioPlayer = [[MPMoviePlayerController alloc] initWithContentURL:audioUrl];    [self.audioPlayer prepareToPlay];    [self.audioPlayer.view setFrame:CGRectMake(0, 0, self.view.frame.size.width-100,     42)];    self.audioPlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth;    [self.view addSubview:self.audioPlayer.view];    [self.audioPlayer play];    // for run application in background    NSError *setCategoryError = nil;    NSError *activationError = nil;    [[AVAudioSession sharedInstance] setActive:YES error:&activationError];    [[AVAudioSession sharedInstance] setDelegate:self];    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];}

I hope it will help you to play audio in background in iphone and simulator as well.