iosswiftmultithreadingavplayer

How to reload data for AVPlayer, if URL failed swift


I have json file with a few URLs (with .mp3) for words. Some of URLs are invalid (or valid, but return error, so i don't get data anyway).

This URL i use to play pronunciation for word. So, I go throw 3 steps:

  1. Finding URL for certain word. If can't find, than nothing happens
  2. Use this URL for initialising AVPlayerItem and preparing AVPlayer. Than just waiting, when user press.
  3. Play sound, when user press onto word

So, first of all, i'm preparing my AVPlayer, to avoid delay in playing.

I'm a little bit confused with multithreading, and i don't understand where should i check if I'm able to play this sound, or not and i should use next URL.

Code:

extension WordCell {

func playPronunciation() {
    player?.play()
    player?.seek(to: .zero)
}

func prepareForPronunciation() {
    if let word = myLabel.text {
        UIApplication.shared.isNetworkActivityIndicatorVisible = true
        DispatchQueue.global(qos: .userInteractive).async { [weak self] in
            let foundURL = self?.findURL(for: word)
            if let url = foundURL {
                let playerItem = AVPlayerItem(url: url)

                //here "playerItem.status" always equals .unknown (cause not initialized yet)
                if playerItem.status == .failed {
                     //self?.giveNextUrl() - will do some code there
                }
                self?.player = AVPlayer(playerItem: playerItem)
                self?.player!.volume = 1.0
            }
            // this is also not correct, because networking continueing
            // but i don't know where to place it
            DispatchQueue.main.async {
                UIApplication.shared.isNetworkActivityIndicatorVisible = false
            }
        }
    }
}

// right now i take URL from file, where there is only one.
// but i will use file with a few URL for one word
private func findURL(for word: String) -> URL? {

    if let path = Bundle.main.path(forResource: "data", ofType: "json") {
        do {
            let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
            let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
            if let jsonResult = jsonResult as? [String: String] {
                if let url = jsonResult[word] {
                    return URL(string: url)
                } else {
                    return nil
                }
            }
        } catch {
            return nil
        }
    }
    return nil
}

}

This is json file with few URLs per a word

"abel": [
    "http://static.sfdict.com/staticrep/dictaudio/A00/A0015900.mp3",
    "http://img2.tfd.com/pron/mp3/en/US/d5/d5djdgdyslht.mp3",
    "http://img2.tfd.com/pron/mp3/en/UK/d5/d5djdgdyslht.mp3",
    "http://www.yourdictionary.com/audio/a/ab/abel.mp3"
],
"abele": [
    "http://www.yourdictionary.com/audio/a/ab/abele.mp3",
    "http://static.sfdict.com/staticrep/dictaudio/A00/A0016300.mp3",
    "http://www.oxforddictionaries.com/media/english/uk_pron/a/abe/abele/abele__gb_2_8.mp3",
    "http://s3.amazonaws.com/audio.vocabulary.com/1.0/us/A/1B3JGI7ALNB2K.mp3",
    "http://www.oxforddictionaries.com/media/english/uk_pron/a/abe/abele/abele__gb_1_8.mp3"
],

So, i need to take first URL and check it. If failed, then take another and check ... and etc, while URLs are over, or find some valid URL. And all this stuff must be done before AVPlayer will try to play sound.

How to implement this and where?

Please, tell and describe resolution in simple words, cause i'm kind of beginner in swift and multithreading.


Solution

  • Solution:

     private var player: AVPlayer? {
        didSet{
            if player?.currentItem?.status == .failed {
                if indexOfURL >= 0 {
                    prepareForPronunciation(indexOfURL)
                }
            }
        }
    }
    

    If URLs are over, set indexOfURL equals -1, otherwise increment it to use next URL in the next function call