Playing Audio in Background at a certain time - swift

Using Swift5.2, Xcode11.4 and iOS13.4,
I try to run Audio at a certain time in the future.
Moreover, the App is in background mode (and the App is completely closed).
The Audiosession must be set up to keep the App responsive and the Audio sound shall start at delay-times up to 1 year.
I tried:
A) The Background Mode "Audio, AirPlay and Picture in Picture" is added:
B) The AudioSession and AVAudioPlayer is configured as follows:
import AVKit
var audioPlayer: AVAudioPlayer?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?)
-> Bool {
do {
//set up audio session
let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(.playback, mode: .default, options: [.defaultToSpeaker, .duckOthers])
try audioSession.setActive(true)
// Start AVAudioPlayer
try audioPlayer = AVAudioPlayer(contentsOf: alertSound)
audioPlayer!.numberOfLoops = -1 // play endlessly
audioPlayer!.prepareToPlay()
let currentAudioTime = audioPlayer!.deviceCurrentTime
let delayTime: TimeInterval = 20.0 // here as an example, we use 20 seconds delay
audioPlayer!.play(atTime: currentAudioTime + delayTime) // delayTime is the time after which the audio will start
}
catch {
print(error)
}
return true
}
.
There are two problems with the code above:
the session-Category .playback does cause the error Error Domain=NSOSStatusErrorDomain Code=-50 "(null)"
the sound only plays when App is in foreground or in background-with-app-still-alive - but unfortunately not in background-with-app-completely-closed
Here my questions:
Why is session-category .playback not working under iOS13.4 ?
Why is the sound not playing when the app is completely closed ? (background-mode) ???
Is it enough to just set the Background-mode tag "Audio, AirPlay and Picture in Picture" ? Or is there some code needed in order to fully implement background mode ? Is there something missing in code in order to make the sound future-start in a fully closed app ?
As for Question 1:
--> I've found a workaround and set the session-Category to .playAndRecored - after that the error goes away
try audioSession.setCategory(.playAndRecord, mode: .default, options: [.defaultToSpeaker, .duckOthers])
The question still remains: why is .playback not working ? And what is the difference between .playback and .playAndRecord ?

Why is session-category .playback not working under iOS13.4 ?
The .defaultToSpeaker option causes that error when used with .playback. I don't know why. If you remove it, it should work.
Why is the sound not playing when the app is completely closed ? (background-mode) ???
The audio session is killed when the app is killed. If you want to play sounds beyond the lifetime of your app, you need to use remote notifications. See "Push notifications method" in this article on building an alarm clock app:
http://andrewmarinov.com/building-an-alarm-app-on-ios/
Note that there are limitations with this method, mainly that it requires the user to have an internet connection at the alarm time to work. This is the current way to setup an alarm beyond the lifetime of the app, other than sending a simple local notification which has its own limitations on sound playback and is silenced by the hardware silent switch.
There are other methods in that link to keep your app open, but it will notify the user (keeping the mic always on, or subscribing to frequent location updates).
Is it enough to just set the Background-mode tag "Audio, AirPlay and Picture in Picture" ? Or is there some code needed in order to fully implement background mode ? Is there something missing in code in order to make the sound future-start in a fully closed app ?
This background mode keeps your app from being suspended while it has an active audio session. It does not prevent your app from being killed. Once killed, your app can't play any sound until it is opened again.

Related

How to start audio recording when app running on background?

I am working on audio recording. when application running on foreground i have to start audio recording and going to background at that time audio recording working fine.
But my question is that how to start audio recording when i am already in background, My audio recording function fired like this:
I have a Bluetooth LE device with buttons and an iOS app. Those two are paired (Bluetooth LE device and the iPhone which runs the iOS app) and the iOS app is listening for events on the Bluetooth LE device, events like a hit of a button.
Now, when the user hits a button on the Bluetooth LE device, the iOS app captures the event and I am able to run code even if the app is in background, but I am not able to start a voice recording.
I have already enable Background Modes:
Here is my Code for Audio Recording:
func startRecording() {
DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime.now(), qos: .background) {
let audioFilename = self.getDocumentsDirectory().appendingPathComponent("recording.m4a")
print("record Audio \(audioFilename)")
let settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 12000,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
do {
self.audioRecorder = try AVAudioRecorder(url: audioFilename, settings: settings)
self.audioRecorder.delegate = self
self.audioRecorder.record()
} catch {
self.finishRecording(success: false)
}
}
}
I can't find proper solution to do that thing, Please suggest me proper way to do that, Thanks in Advance.
This is not possible to do without an explicit UI interaction for security reasons, or else it would be possible to "spy" on a person (once paired, a person outside of a room could start recording and listen to what happens inside the room).
Workarounds could be:
send a local notification e.g. "The device is ready to record, want to start?". Once you tap this, the app opens in foreground, and should be able to start the recording.
use CallKit (suitable for phone-like use cases). When the user presses "Accept" in the CallKit system UI, it is possible to start recording audio even in background.
Here are my solutions:
Trigger a CallKit call from you app and control your headphone to answer the call.
Use your headphone to record and transfer voice data with bluetooth channel.

What would cause WatchOS background audio to play ONLY while connected in debug session with XCode and fail otherwise

I'm using XCode 11.4, WatchOS 6.2, Swift/UI to build a watch app that plays background audio via speaker or BlueTooth pending user choice. Here are the scenarios:
Pass:
From XCode run on target watch (series 5)
Start audio
Screen On: Audio plays via speaker or blueTooth
Screen Off (drop wrist): Background audio plays via speaker or blueTooth
Fail:
Launch app from watch as user would (not run as XCode target)
Start audio
Screen On: App plays via speaker or blueTooth
Screen Off (drop wrist): Background audio dies for speaker or blueTooth output scenarios
As far as I can tell I've:
Set the background capabilities correctly
Set Session category and activated prior to playing audio
do {
try session.setCategory(AVAudioSession.Category.playback,
mode: .default,
policy: useBlueTooth ? .longFormAudio : .default,
options: [])
} catch _ {
fatalError("AudioSession Failed.")
}
session.activate(options: []) { (success, error) in
guard error == nil else {
print("Audio Session Activation Error: \(error!.localizedDescription)")
// Handle the error here.
return
}
}
What might cause this failing behavior when not launched via XCode? Advice on how to debug/fix please. Thanks!

Recording interrupted by multitasking and content resizing

A try to start a screen recording with RPScreenRecorder. I got the following error:
Recording interrupted by multitasking and content resizing
func startRecording() {
let recorder = RPScreenRecorder.shared()
recorder.startRecording(handler: { (error) in
if let unwrappedError = error {
print(unwrappedError.localizedDescription)
} else {
}
})
}
Before iOS 12.0 everything worked fine. From the update I get the error above.
My app has been rejected from App store for the same reason. So far the only workaround is to reboot the device.
I had a similar problem and here is how I solved it.
go to project then targets then capability switch on Background mode then enable audio and VOIP. It should work
I've done a lot of research on the errors and posted the solution Here.
For now my screen recording feature is bug free. But who knows what comes with the new OS updates
We've been rejected same issue several times.
But we found a senario to re-produce as bellow,
We reported it on Resolution Center in App Store Connect, then passed.
connect iOS(12.4) device to host launched XCode 10.3
(regardless of opened related project)
cold boot iOS device.
launch app and start recording video ASAP(until 30sec after booted)
Now iOS13, we don't face this error at the above senario.

Audio session mixes with others even when its category is set to playback (Swift)?

I am using AudioKit to manage the sound in my app. I am trying to show play/pause buttons on the lock screen, which requires the audio session to not be mixable with other audio sessions that may be running. To do this, I set my audio session to the category 'playback', which is not supposed to be mixable with others. Here is my method that sets this up:
private func configureAudio() {
do {
try AKSettings.setSession(category: .playback, with: [])
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: [])
} catch {
print(error)
}
AKSettings.playbackWhileMuted = true;
}
This works for making the audio play even when the app is not in focus, but the audio still mixes with other sessions even though it's supposedly set to 'playback'. What am I doing wrong?
You've forgotten to activate your audio session, add this to your do block:
try AVAudioSession.sharedInstance().setActive(true)

How to keep AVPlayer video playing when app goes background

I'm trying to keep the video in AVPlayer playing when app goes background, but it always stops. I use Swift. I've found a lot of examples, but nothing works.
I've turned on "App plays audio or streams audio/video using AirPlay" in Info.plist and added this code to the didFinishLaunchingWithOptions in AppDelegate:
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
}
catch {
/*Error*/
}
What is the proper way to keep video playing?