Beginnerrrrrr
Beginnerrrrrr

Reputation: 595

Using AVAudioPlayer fail to play .m4a by Swift 4

I use Alamofire to download the iTunes search Api's trial music.
And when I download finished, I want to play the music.
I try to fix it, but it also no sounds to play.
How to solve this problem?
Thanks.

import UIKit
import AVFoundation
import Alamofire
import CryptoSwift

class FirstViewController: UIViewController {

    let urlString = "https://audio-ssl.itunes.apple.com/apple-assets-us-std-000001/AudioPreview18/v4/9c/db/54/9cdb54b3-5c52-3063-b1ad-abe42955edb5/mzaf_520282131402737225.plus.aac.p.m4a"

    override func viewDidLoad() {
        super.viewDidLoad()

        let destination: DownloadRequest.DownloadFileDestination = { _, _ in
            let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
            let fileName = self.urlString.md5()
            let fileURL = documentsURL.appendingPathComponent("\(fileName).m4a")
            return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
        }

        Alamofire.download(urlString, to: destination).response { response in

            if response.error == nil {

                var audioPlayer: AVAudioPlayer!

                do {
                    audioPlayer = try AVAudioPlayer(contentsOf: response.destinationURL!)
                    audioPlayer.prepareToPlay()
                    audioPlayer.play()
                } catch {
                    print("Error:", error.localizedDescription)
                }

            }
        }

    }
}

Upvotes: 0

Views: 3496

Answers (2)

mag_zbc
mag_zbc

Reputation: 6982

The problem is caused by the fact that audioPlayer is a local variable, therefore it gets deallocated when you leave the scope of completion closure. Because audioPlayer isn't retained anywhere else, the moment you leave the closure's scope, audioPlayer reference count is equal to 0, which causes ARC to deallocate it.

Also, you use force unwrapping operator - ! - a lot, which is:
1) incorrect
2) unsafe
Use either if let construct or guard statement

What you need to to is store the player as instance variable of your FirstViewController class.

class FirstViewController: UIViewController {

    let urlString = "https://audio-ssl.itunes.apple.com/apple-assets-us-std-000001/AudioPreview18/v4/9c/db/54/9cdb54b3-5c52-3063-b1ad-abe42955edb5/mzaf_520282131402737225.plus.aac.p.m4a"

    var audioPlayer : AVAudioPlayer?

    override func viewDidLoad() {
        super.viewDidLoad()

        // (...)

        Alamofire.download(urlString, to: destination).response { [weak self] (response) in

            if response.error == nil {

                guard let url = response.destinationURL else { return }

                do {
                    self?.audioPlayer = try AVAudioPlayer(contentsOf:  url)
                    self?.audioPlayer?.prepareToPlay()
                    self?.audioPlayer?.play()
                } catch {
                    print("Error:", error.localizedDescription)
                }
            }
        }
    }
}

Upvotes: 2

SPatel
SPatel

Reputation: 4946

Just move audioPlayer to controller

class FirstViewController: UIViewController {

    let urlString = "https://audio-ssl.itunes.apple.com/apple-assets-us-std-000001/AudioPreview18/v4/9c/db/54/9cdb54b3-5c52-3063-b1ad-abe42955edb5/mzaf_520282131402737225.plus.aac.p.m4a"
    var audioPlayer: AVAudioPlayer?

   //Downloading code......
}

Upvotes: 0

Related Questions