3

I have a requirement where my app connects to a country channel (USA) and starts playing records from the channel. This is basically a channel which is run by users, the users upload their records to channel and they are played one by one. The user who connects to channel they start listening to channel.

The server sends the iOS app the URLs for the record that needs to be played via sockets, the iOS app creates AVQueuePlayer to play the URL's (using AVPlayerItems) one by one.

If I keep app in background when the channel is full of records for almost 1 day or so, the app keep running and keep playing all the records one by one. I know that AVQueuePlayer takes care of running the app all the time without killing as it receives new player items to play.

But if there are no records in channel and if user connects to channel, then app doesn't play the records in background if the idle time of the app exceeds 10 minutes.

I have written code with background task identifier which keeps my socket connect open so that new record URLs can be received all the time.

I see some of the crash reports in my device which says "AppName(my app) has active assertions beyond permitted time"

So can I know what wrong is going on here.

I am posting the background task code as well

- (void)keepBroadcastPersistentConnection {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    if(self._bgTaskIdentifier)
        self._bgTaskIdentifier = UIBackgroundTaskInvalid;
    self._bgTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{

        [[UIApplication sharedApplication] endBackgroundTask:self._bgTaskIdentifier];
        self._bgTaskIdentifier = UIBackgroundTaskInvalid;
        CGLog(@"========================================end bg task at time %@", [NSDate date]);
        CGLog(@"Time taken by app to run in bg is %f seconds", [[NSDate date] timeIntervalSinceDate:self.date]);
    }];

    [[BroadcastSocketConnecter sharedSocketConnecter].socketIO sendHeartbeat]; // this keep the socket alive
    self.date = [NSDate date];
    CGLog(@"========================================begin bg task at time %@", self.date);
});
}

Thanks

MZimmerman6
  • 8,445
  • 10
  • 40
  • 70
Naveen
  • 636
  • 8
  • 28
  • 1
    There are many post on stackoverflow that tell you why you should not do this and what is possible. Try the search and don't make you the app run forever in the background. – rckoenes Sep 18 '13 at 12:02
  • Please explain this: *But if there are no records in channel and if user connects to channel, then app doesn't play the records in background if the idle time of the app exceeds 10 minutes.* I don't get what's happening. On the other hand, you cannot have your application run in background for more than 10 or so minutes if it doesn't perform some permitted background task. But you already described this, so I don't get what your question is. – allprog Sep 18 '13 at 12:16
  • @allprog : This means that if app connects to some channel and nothing is played in the channel and if user takes the app the background at this time, the app keeps the sokcet connection alive for only 10 mins, after that it disconnects. At this point if some one uploads a record to channel then it is not played in the device. – Naveen Sep 18 '13 at 12:39
  • 1
    Is the data cosumed by the receiver socket and is the new item successfully added to the queue? – allprog Sep 18 '13 at 12:47
  • @allprog: yes, it is consumed and item gets created and added into queue player. there is no issue in it. I tested the app running in background while it connected to some channel which contains around 10 hrs of records, the socket keep running and app keep playing the records for 10 hrs. – Naveen Sep 18 '13 at 13:06
  • Then the question is whether there is some policy that disables the starting of audio play when the app is backgrounded. I can imagine there is. I wouldn't want my phone start playing music at random times but if it is playing already, then it can keep doing so. – allprog Sep 18 '13 at 13:27
  • @allprog: There is no logic as of now which disables the audio starting of the audio when the app is in background. – Naveen Sep 18 '13 at 13:54
  • Look at my answer, I think it explains exactly what you are seeing. – allprog Sep 18 '13 at 13:56
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/37609/discussion-between-nkd-and-allprog) – Naveen Sep 18 '13 at 18:54

1 Answers1

4

From the audio session programming guide:

Why a Default Audio Session Usually Isn’t What You Want

Scenario 3. You write a streaming radio application that uses Audio Queue Services for playback. While a user is listening, a phone call arrives and stops your sound, as expected. The user chooses to ignore the call and dismisses the alert. The user taps Play again to resume the music stream, but nothing happens. To resume playback, the user must quit your application and restart it.

To handle the interruption of an audio queue gracefully, implement delegate methods or write an audio session callback function to allow your application to continue playing automatically or to allow the user to manually resume playing. See “Responding to Audio Session Interruptions.”

Shortly, the solution would be to implement the AVAudioSessionDelegate protocol'a beginInterruption and endInterruption methods. However, the delegate property of the AvAudioSession class was deprecated in iOS6 and Notifications should be used instead. Namely, you are interested in the AVAudioSessionInterruptionNotification

Solution. According to this story if the playback stops, then you should explicitly activate the audio session again to prevent your app from being terminated.

Below is the source for the delegate implementation but the logic doesn't change much with the notifications so I feel it's still a good source for info.

- (void) beginInterruption {
    if (playing) {
        playing = NO;
        interruptedWhilePlaying = YES;
        [self updateUserInterface];
    }
}

NSError *activationError = nil;
- (void) endInterruption {
    if (interruptedWhilePlaying) {
        BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
        if (!success) { /* handle the error in activationError */ }
        [player play];
        playing = YES;
        interruptedWhilePlaying = NO;
        [self updateUserInterface];
    }
}

Old response which is still valid but not an elegant solution

You cannot start playing audio in the background. This answer explains what I mentioned in my comment above: https://stackoverflow.com/a/16568437/768935 Doing tricks with the AudioSession does not seem to have an effect on this policy.

As a solution, you need to keep playing audio. If there is no item in the queue, then insert the "silence" audio track. However, I have my doubts that the app with this trick will be admitted in the App Store. It may be better to inform the user that audio playback will be resumed on if the app is started again.

Community
  • 1
  • 1
allprog
  • 16,540
  • 9
  • 56
  • 97