I am using AVAudioPlayer
to play a looping sound. My current code has this line: audioPlayer.numberOfLoops = -1
, which causes indefinite loop. I use a timer to stop the looping audio after a certain period of time. This works when app is in the foreground, but not after it goes to the background.
The background capabilities of my app are:
Despite the background mode is enabled and audio is playing while the app goes to the background, the app seems like it is suspended right after applicationDidEnterBackground
was called. I say so because (1) timer stops printing output at every second to the console (2) audio keeps playing indefinitely even after timer should have ended.
I have read here that this must not be the case, app should not be suspended if an audio is playing in the background, but this seems like not the case in my app.
What am I missing here? What should I do to make the timer actually run in the background, besides playing a looping sound if it is not enough?
The code to run the audio:
var audioPlayer = AVAudioPlayer()
do {
let (fileName, fileExtension) = fileNameExtension(fileName: soundFileName)
let soundURL = URL.init(fileURLWithPath: Bundle.main.path(forResource: fileName, ofType: fileExtension)!)
audioPlayer = try AVAudioPlayer(contentsOf: soundURL)
audioPlayer.prepareToPlay()
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(.playback, mode: .default, options: [.allowAirPlay, .allowBluetooth, .allowBluetoothA2DP, .mixWithOthers])
try AVAudioSession.sharedInstance().setActive(true)
} catch let error as NSError {
print("Fetch error: \(error) description: \(error.userInfo)")
}
} catch let error as NSError {
print("Fetch error: \(error) description: \(error.userInfo)")
}
audioPlayer.numberOfLoops = -1
audioPlayer.play()
The code to run the timer. This code runs while app is in the foreground.:
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { [weak self] _ in
self?.updateTimer()
})
RunLoop.current.add(timer, forMode: .common)
timer.tolerance = 0.2
updateTimer:
@objc func updateTimer() {
remainingTime -= 1
if remainingTime <= 0.0 {
complete()
}
}
The question has some false premises:
applicationDidEnterBackground
does not mean your app will be suspended. It usually does, but if you have the audio background mode capability and the playback
audio category, and you are playing sound when you go into the background, you are not suspended; instead, you run in the background.
A timer that is started before you go into the background does work in the background under those circumstances.
So if you're hearing sound in the background from your app but your timer is not firing, that must be due to something else you're doing. Perhaps you've configured things so that the timer is invalidated when you go into the background?