Search code examples
iosswiftavaudioplayerdispatch-queueios-multithreading

Playing instance of AVAudioPlayer from background thread sometimes fails


I am trying to play a sound from a background thread using an instance of an AVAudioPlayer in Swift 5. Most of the time, it will be successful. But once in a while the audio player fails to play a sound.

It appears as though calling play() twice, or prepareToPlay() and then play() in a row somehow fixes the problem, but this doesn't seem like a good solution to me, as I am worried that this still doesn't guarantee that it will play successfully.

func setUpAudio() {
    AVAudioSession.sharedInstance().perform(NSSelectorFromString("setCategory:error:"), with: AVAudioSession.Category.playback)
    try! AVAudioSession.sharedInstance().setActive(true)
    NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption), name: AVAudioSession.interruptionNotification, object: nil)
}

func startNewAudio() {
    let destinationData = Destinations.getDestinations()
    var audio = destinationData[currentStop]!["audio"] as! String
    if inTransit {
        audio = destinationData[Paths.tourOrder[currentIndex + 1]]!["transit_audio"] as! String
    }
    let sound = NSURL(fileURLWithPath: Bundle.main.path(forResource: audio, ofType: "mp3", inDirectory: "audio")!)
    try! audioPlayer = AVAudioPlayer(contentsOf: sound as URL)
    audioPlayer.delegate = self
    currentTime = 0
    setPlay()
}

func setPlay() {
    paused = false
    pauseButton.setImage(UIImage(named: "pause"), for: .normal)
    pauseButton.isEnabled = true
    audioPlayer.currentTime = currentTime
    DispatchQueue.global(qos: .background).async {
        self.audioPlayer.play()
        print(self.audioPlayer.isPlaying) // returns false sometimes
    }
}

I call the methods in this order:

setUpAudio()
startNewAudio()
setPlay()

I can't figure out the problem—I really want to just call play() once and have everything work fine.


Solution

  • There is no reason to say play on a background thread; playing a sound does not block the main thread. Moreover there is no reason to suppose that AVAudioPlayer is thread safe. And YOUR code is certainly not thread safe, as you are talking to the audio player simultaneously from different threads. So you’re using threads incorrectly. There’s no point discussing wrong behavior results. Take away your background thread and try your code doing things the right way. If there is still a problem we can talk about that.