Prevent AirPlay from sleeping - ios

I have an app that uses AirPlay. I am using the iPad to present data on a TV. The user sets up the demo and then puts the device down and the app should take care of updating the TV even in the background state. The app is processing data in the background and I can see it is always being updated. However the AirPlay disconnects after a few minutes if nothing on the screen changes.
How can I prevent AirPlay from disconnecting?
Note:
I am not streaming video or audio. I am only presenting data using views.
Update:
After more twiddling I found that the background task dies after 10 minutes and that is when the screen dies. I tried to play a sound like https://stackoverflow.com/a/15732710/1449607 described but the AirPlay still disconnected. I am now toggling a pixel and now it seems that the screen is staying connect. There must be a better solution for displaying slowly updating data to AirPlay.

Related

Turning off audio background mode triggers kMIDINotPermitted even though background audio is not needed

I've got a MIDISampler that is triggered by a MIDICallbackInstrument. I don't want my app to work in the background since it is an interactive ear training game. Unfortunately if I disable "Audio, AirPlay, and Picture in Picture" I get the following error:
CheckError.swift:CheckError(_:):176:kMIDINotPermitted: Have you enabled the audio background mode in your ios app?
Also the sampler plays fixed pitch sine waves instead of samples.
The solution would be to just turn on the audio background mode though it is not needed. Everything works fine, no errors. Unfortunately Apple is rejecting my app in that case since it's not using background audio.
Does anyone know a work around for this?
You need background audio enabled for MIDISampler. As long as your app is stopping the audio engine when the app moves into the background, they should allow it. I would explain your use case and appeal the rejection.

HLS audio stream fails to resume play after time in background (AVPlayer refuses to buffer)

I have an iOS app that is designed to play HLS audio stream content.
The app supports time-shifting - you can skip backward, skip forward, scrub backward/forward, as well as pause the stream and unpause to where you left off.
The app works fine when operating in the foreground. It also works fine when actovely playing in the background (I have the background audio entitlement set correctly).
However, if the app is put into the background for more than a few moments (~30 seconds seems to do it) with the audio paused and either A) the user un-pauses the audio, or B) the user returns the app to the foreground and attempts to un-pause it from there, the audio does not play even after waiting a few moments. Note that the app process is not killed during this time.
To pause and unpause, I am using AVPlayer's pause and play methods.
While looking into it, I verified that the buffer (determined by looking at loadedTimeRanges) is filled during normal playback, but is quickly emptied shortly after the paused app is put in the background. If the app is returned to the foreground quickly enough, the buffer begins to fill again and playback can resume. Otherwise - if the user moves the app to the background for ~30 seconds - the buffer never refills at all and attempting to play again fails until the AVPlayerItem is re-configured.
I realize that there is no guarantee that the buffer will always have content (ie. it could be emptied to minimize memory footprint, which I suspect is the case here), but would expect AVPlayer to begin to start loading audio from it again as needed when playback resumes. Even if it has been in the background for a while.
Does anybody have an idea why this is occurring or how to work around it?
Note: I have created a simple sample iOS 14+ Xcode project that exhibits the problem using a known HLS stream. Tap 'Configure' to load the URL, then play/pause to exhibit the issue (I added console output showing the state of loadedTimeRanges).
https://tapestryapps.com/AudioTestbed.zip
Thank you.
If anyone is still experiencing this when working with HLS and changing the playerViewController?.player?.currentItem?.preferredPeakBitRate = 1 to safe data when backgrounding the application a workaround for this is to lower the buffer size playerViewController?.player?.currentItem?.preferredForwardBufferDuration = 10
When returning back to foreground all values can be set again to the default of 0
As an alternative you could also replace the playerViewController?.player?.currentItem but this would then result in video and audio buffering again.
The code works for me on iOS 13.5.1 and iOS 14.4, but fails on 14.0.1.
It looks like an iOS bug. You could work around this by recreating the AVPlayerItem when returning to the foreground on affected systems.

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.

iOS: background play from the browser?

Some iOS apps like Pandora continue playing audio after the app screen has been exited, and even after the device has been locked.
Is it possible to keep audio playing in this way from a browser tab? In other words, can I code an HTML5 page that plays audio and continues playing even after the iPhone is locked?
Yes, audio playing in a mobile Safari tab will continuously play even when multi-tasking or locking your screen. However, if you save the bookmark to the home screen, open, play audio, and then hop out to multi-task, the audio fades out.
Not possible... the browser displaying your page controls what happens when it is deactivated (sent to the background). HTML5 won't touch the behavior of the underlying browser as it relates to system events, so the only way to control background activity in iOS is to write a native app.

Resources