darkginger
darkginger

Reputation: 690

Using AVAudioPlayer with Dynamic URL in Swift 3 causing Thread Errors

I am new to Swift and making an audio app using AVAudioPlayer. I am using a remote URL mp3 file for the audio, and this works when it's static.

For my use case, I want to pull a URL for an mp3 file from a JSON array and then pass it into the AVAudioPlayer to run.

  1. If I move the AVAudioPlayer block into the ViewDidLoad and make the mp3 file a static URL, it will run fine.

  2. Then, when I move this code into my block that extracts an mp3 url from JSON, I can print the URL successfully. But when I pass it into my audio player, problems arise. Here's the code.

    override func viewDidLoad() {
    super.viewDidLoad()
    
        let url = URL(string: "http://www.example.com/example.json")
            URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
                guard let data = data, error == nil else { return }
    
                let json: Any?
                do{
                    json = try JSONSerialization.jsonObject(with: data, options: [])
                }
                catch{
                    return
                }
    
                guard let data_list = json as? [[String:Any]] else {
                    return
                }
                if let foo = data_list.first(where: {$0["episode"] as? String == "Example Preview"}) {
    
                    self.audiotest = (foo["audio"] as? String)!
                    print(self.audiotest) // this prints 
    
                    // where i'm passing it into the audio player
                    if let audioUrl = URL(string: self.audiotest) {
    
                        // then lets create your document folder url
                        let documentsDirectoryURL =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    
                        // lets create your destination file url
                        let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
    
                        //let url = Bundle.main.url(forResource: destinationUrl, withExtension: "mp3")!
    
                        do {
                            audioPlayer = try AVAudioPlayer(contentsOf: destinationUrl)
    
                        } catch let error {
                            print(error.localizedDescription)
                        }
                    } // end player
    
    // ....
    

Specifically, I get an error Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value when clicking a play button IBAction that is connected to the audio player. Finally, that action function looks like this:

  @IBAction func playPod(_ sender: Any) {
    audioPlayer.play() 
}

Do you know where I'm going wrong? I'm confused as to why I can't print the URL and also get a response that the URL is nil in the same block, but maybe that's an asynchronous thing.

Upvotes: 0

Views: 812

Answers (1)

Shehata Gamal
Shehata Gamal

Reputation: 100503

The problem is that you didn't save the mp3 file to documents and trying to play it

this line

    audioPlayer = try AVAudioPlayer(contentsOf: destinationUrl)

assumes that there is a saved mp3 file in that path , but acutally there is no files you appended the audio extension on the fly

besides for steaming audio from a remote server, use AVPlayer instead of AVAudioPLayer. AVPlayer Documentation

Also try this with urls parsed from json

  var urlStr = (foo["audio"] as? String)!

  self.audiotest = urlStr.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)

Upvotes: 1

Related Questions