MPMusicPlayerControllerNowPlayingItemDidChange in background - ios

Is there a way to get the MPMusicPlayerControllerNowPlayingItemDidChange notification while the app is in the background?
I have an app that needs to be able to pause the music once a song has ended while in the background or when the screen is locked. I'm using a systemMusicPlayer to play the music.
I've tried adding the audio background capability and including a call to beginBackgroundTask in my applicationDidEnterBackground but that doesn't work for extended periods of time.

When in background, your app might be killed at any time, so you do not want to depend on acting in background.
How I understood, what you want to achieve is that when your app goes to background, the currently playing music goes on but stops after the currently playing song finished.
MPMusicPlayerController.systemMusicPlayer() is playing a queue of songs ("Playlist"). So I would try to manipulate this queue in applicationWillResignActive() to not have a song after the currently playing one .
I did not test this and I am not sure whether or not this is possible through public API.

Related

How to resume audio playback of other apps after deactiving the audio session and how to start audio playback in the background without mixing?

I am building a iOS app with Ionic and Capacitor. I use AVFoundation inside a Capacitor plugin which provides a public method to play an audio file accessible at a remote URL. The basics work, the audio file is successfully played. Now I want to correctly handle interruptions and background audio. By background audio I mean start playing an audio file while the app is in the background and not continue playing it in the background when it was started in foreground before. To summarize, the app should behave the following way:
If another app (e.g. Spotify) is playing audio and my app wants to play audio, the other app should be interrupted and my app's audio should start playing without mixing.
If my app's audio is finished playing, the other app's audio should resume.
My app must be able to start playing audio without mixing when it is in the background.
I want to show a play/pause button and the audio title on the lockscreen and in the notification center.
I added the background mode audio capability. I use AVPlayer, AVAudioSession, MPNowPlayingInfoCenter and MPRemoteCommandCenter, followed best practices, especially regarding activation and deactivation of an audio session and interruption handling. I followed this question and this question. I tried several solutions, but never managed to achieve all of the above goals. What I found out so far is the following:
Goal 1. above is easy to achieve. Simply activate an audio session without mixing and start playing the audio.
After the audio of my app is finished, I deactivate the audio session. Unfortunately, I can only achieve goal 2. above if I don't use remote commands nor notification info. This means, my app then has no audio control UI on the lockscreen nor in the notification center. Somehow the configurations of MPNowPlayingInfoCenter and MPRemoteCommandCenter seem to affect the interruption behavior. As far as I could see, Apple documentation doesn't say anything about this.
I only manage to start playing audio in the background when setting the option .mixWithOthers. Apple documentation doesn't say anything about this, either.
Any help would be greatly appreciated.
Rony

How to loop a queue of songs in AVQueuePlayer even when device is locked?

I already have a looping implementation, and it works when the device is open. I listen for the AVPlayerItem.DidPlayToEndTimeNotification and handle the looping in that Callback. But as soon as the device is locked, it the AVQueuePlayer does not play. Is this somehow related to iOS backgrounding and how apps function when backgrounded?
Normally, Apple does not allow apps to run in background. If you want to play a song in background, you should set the Required Background Modes property in the application's Info.plist.
Audio - Music players and other applications that work with audio
content may be registered to continue playing audio even when the app
is no longer in the foreground. If an app in this category attempts to
do anything other than play audio or download while in the background,
iOS will terminate it.
Look at the document and ask for permission.
I fixed the problem. When an AVPlayer stops playing, the app is suspended by iOS even with Background Modes enabled. My looping logic is implemented right after the AVPlayer plays the last item so it doesn't get executed in the background.

Audio voiceover when iPhone is locked

Solution below
I am working on a running app which gives voiceover instruction at the start of each exercise. It works as intended when the device is active but doesn't work when the phone is locked. [Current audio continues but no future audio plays.]
Question
How can I start playing an audio voiceover while the users iPhone is locked.
Currently I track the workout using Timer.scheduledTimer for 2 timers I display [current activity and the overall workout]. When the timer hits a pre-defined time towards the end of one activity, the voiceover audio plays to introduce the next activity. The timer firing is what starts the audio and I believe this is the issue. I can pause the workout, skip sections and all works fine - until the device is locked.
If an audio voiceover is playing when I press the home button or lock the iPhone it continues to play as expected. The issue is then that the timer doesn't fire so the next audio voiceover [in say 90 seconds] is never played.
Some of the answers and comments I've read have said that this functionality just isn't possible in iOS. The Couch to 5k app https://itunes.apple.com/gb/app/one-you-couch-to-5k/id1082307672?mt=8 uses this functionality so I know it's possible to achieve, I just don't know how they're doing it. [It will be a dedicated member that downloads it to see what I mean :)]
From searching SO I've read a lot of post saying that Timer or NSTimer can't run when the app is in the background or the phone is locked. Any posts that say it can work are old and based on iOS4/5
I've read about suggestions of using silence and essentially having 1 long audio file. While this would pose some new challenges for skipping the sections in the workout I've also read comments that say this behaviour would not pass Apple's testing of the app for the AppStore. The 3rd downside being that my audio file size would increase.
An option I've seen is local notifications, however I'm yet to see an example of one used to play audio voiceover when the app is in the background.
How can I achieve the same functionality as the Couch to 5k app?
Thanks
Ok, I worked up a solution to play audio prompts to the user throughout the course of a 30 minute workout, even if the iPhone is locked or the app is in the background. The app has been submitted and approved for the AppStore.
To get the app to perform as required I use 2 AVAudioPlayers; one plays the voiceover, the other plays a 15 minute track of silence [as it’s a separate track and just silent it’s a low quality .mp3 and only added 900kb to the app size]. 15 minutes well exceeds any interval between voiceovers so it works perfect.
When the user starts an exercise the first audio prompt is played. From that point, until the user stops the exercise there will be audio playing in the background. Because background audio is playing, the Timer still operates.
When a voiceover finishes, the AVAudioPlayerDelegate method audioPlayerDidFinishPlaying is called. Within this method I reset the audio properties [I’ll explain why shortly], instantiate my silent AVAudioPlayer, and start playing the 15 minute track of silence. Playing another track using this delegate method means that there is no real break in background audio so the app is kept alive. With the app ‘alive’ the timer continues to countdown. When it’s time for another voiceover the timer calls a method which resets the audio category, instantiates my voiceover AVAudioPlayer and plays a voiceover. This process is repeated until the final voiceover. When that completes no further silence is played and the app can safely be backgrounded.
The audio voiceover needs to duck the users music [reduces the music volume so that the audio can be clearly heard]. The silent audio shouldn’t do that, music should continue to play at full volume. As both AVAudioPlayers use the AVAudioSession.sharedInstance I found that I had to deactivate the sharedInstance, reset the properties, with or without .duckOthers, and then reactivate the sharedInstance before instantiating my AVAudioSession. That then gave me the desired result. Constant background audio that only ducked the users music when a voiceover played.
do{
try AVAudioSession.sharedInstance().setActive(false)
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [.interruptSpokenAudioAndMixWithOthers, .duckOthers])
try AVAudioSession.sharedInstance().setActive(true)
} catch{
print(error.localizedDescription)
}
I know that some said Apple do not allow silent audio, however, in this circumstance the silent audio only plays for the duration of the exercise routine. As soon as it ends the app can terminate, thereby not keeping the app alive any longer than necessary.
My understanding of VoiceOver leads me to the way that only a foreground application can control what it reads out.
You need focusing with VoiceOver to make it speak and you focus nothing when the device is locked or your app is in the background mode.
I suggest to take a look at the speech synthesis or the AVAudioSession class to enable background audio: both might be good workarounds to take over in the background mode: just try and let us know. ;o)

Keep the app awaken in background when [MPMusicPlayerApplicationController applicationQueuePlayer] is used to play the music

With iOS 10.3 Apple released new APIs in MediaPlayer framework that brings more control/power to the developer that want to play music stored on Apple Music/iTunes Match. I’m talking about new applicationQueuePlayer and applicationMusicPlayer of MPMusicPlayerApplicationController.
The problem is of course with the multitasking. I'm building basically an alternative to Apple's own music player. But also I need to execute some code (mostly network connections) when the music is playing. Those new APIs requires to turn on background modes for the app (Audio, AirPlay and Picture in Picture), so music is playing when the user goes to home screen, but the app itself is suspended.
I've figured out that I can play silent audio so the app itself stays alive and maybe, maybe the app will be approved (as technically it's playing music in background and background mode capability must be turned on anyway). But I don't want to consume user's battery when the music is paused.
Even funnier: it seems, that new API is designed for the apps that stay alive in background, because on iOS 10.3.3 after changing the track via Control Center and tapping on the name of the song the system music app is opened instead of my app.
Imagine, that user pause the music from control center before arriving to work, then resumes it from control center. I don't want to eat user's battery when he's at work.
My question is: is there any way to keep the app live and respond to actions taken by the user?
"The music player does not affect the Music app’s state. When your app moves to the background, the music player stops playing the current media."
https://developer.apple.com/documentation/mediaplayer/mpmusicplayercontroller/2817540-applicationqueueplayer
systemMusicPlayer will continue in the background, BUT, you have no access, or I think now (iOS11) limited access to the queue.
I think you still need to use some form of AVPlayer ( https://developer.apple.com/documentation/avfoundation/avplayer ) if you want granular control over what goes on, but then you will need to implement all the things that update the lock screen etc on your own.

Play Audio when suspending to background

I have an app that under certain circumstances needs to play audio the moment it is pushed into background.
I can't use any background key, as I don't fulfill any requirements for these and Apple rejected my app, when I tried to use the background audio key.
Currently I'm start playing the audio file and then request extra processing time within applicationDidEnterBackground: The audio never gets played even though I have the processing time and the app is still running (I've used an NSTimer to check every 2 seconds).
As audio mode I'm using AVAudioSessionCategoryPlayback with AVAudioSessionCategoryOptionMixWithOthers.
With active background audio mode this works fine.
I also know, that I could use local notification and play the audio file there (as it is under 30 seconds), but that would be my last resort.
Does Apple prevent audio from being played in applicationDidEnterBackground: ?
I get a callback for audioPlayerBeginInterruption: immediately.
I've seen:
Why does the following code to play audio in the background not work?
But this doesn't seem to work anymore on iOS8.
Thanks :)
Your app should not be playing a sound (and certainly not a sound of any length) just because the user backgrounds it, and now Apple has succeeded in stopping you from doing this. You should accept this, abandon your attempts to work around this perfectly reasonable restriction, and move on.

Resources