Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to download and save an audio file and then play it in swift?

I am trying to download an audio file from the internet and save it onto the phone. This is the download function:

func download() {
    if let audioUrl = downloadUrl {

        // 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)
        print(destinationUrl)

        // to check if it exists before downloading it
        if FileManager.default.fileExists(atPath: destinationUrl.path) {
            print("The file already exists at path")

            // if the file doesn't exist
        } else {

            // you can use NSURLSession.sharedSession to download the data asynchronously
            URLSession.shared.downloadTask(with: audioUrl, completionHandler: { (location, response, error) -> Void in
                guard let location = location, error == nil else { return }
                do {
                    // after downloading your file you need to move it to your destination url
                    try FileManager.default.moveItem(at: location, to: destinationUrl)
                    print("File moved to documents folder")
                } catch let error as NSError {
                    print(error.localizedDescription)
                }
            }).resume()
        }
    }
}

Then, after I close and open the app, I use the following function to retrieve the url and play it using an AVPlayer:

func getUrl2() {
    if let audioUrl = downloadUrl {

        // then lets create your document folder url
        let documentsDirectoryURL =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

        destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)

        if let u = self.destinationUrl {
            let player = AVPlayer(url: u)
            print(u)
            print("Bouta play")
            print(CMTimeGetSeconds(player.currentItem!.duration))
            player.play()
        }
    }
}

The duration that keeps getting printed out is "nan". Is there a way to check if the audio file is actually downloading? Or could it be a problem with retrieving the file after the download? Thanks in advance.

like image 565
Tob Avatar asked May 17 '19 22:05

Tob


1 Answers

First of all you have to check for the URL is not empty with the below logic:

if !link.isEmpty{
        checkBookFileExists(withLink: link){ [weak self] downloadedURL in
            guard let self = self else{
                return
            }
            play(url: downloadedURL)
        }
    }

Then checkBookFileExists function will check if the file already saved or not before download it again:

    func checkBookFileExists(withLink link: String, completion: @escaping ((_ filePath: URL)->Void)){
    let urlString = link.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
    if let url  = URL.init(string: urlString ?? ""){
        let fileManager = FileManager.default
        if let documentDirectory = try? fileManager.url(for: .documentDirectory, in: .userDomainMask, appropriateFor:nil, create: false){

            let filePath = documentDirectory.appendingPathComponent(url.lastPathComponent, isDirectory: false)

            do {
                if try filePath.checkResourceIsReachable() {
                    print("file exist")
                    completion(filePath)

                } else {
                    print("file doesnt exist")
                    downloadFile(withUrl: url, andFilePath: filePath, completion: completion)
                }
            } catch {
                print("file doesnt exist")
                downloadFile(withUrl: url, andFilePath: filePath, completion: completion)
            }
        }else{
             print("file doesnt exist")
        }
    }else{
            print("file doesnt exist")
    }
}

Then if the file doesn't exists you will download it with the below function:

func downloadFile(withUrl url: URL, andFilePath filePath: URL, completion: @escaping ((_ filePath: URL)->Void)){
    DispatchQueue.global(qos: .background).async {
        do {
            let data = try Data.init(contentsOf: url)
            try data.write(to: filePath, options: .atomic)
            print("saved at \(filePath.absoluteString)")
            DispatchQueue.main.async {
                completion(filePath)
            }
        } catch {
            print("an error happened while downloading or saving the file")
        }
    }
}

That function will save it and you can play it with:

  func play(url: URL) {
    print("playing \(url)")

    do {

        audioPlayer = try AVAudioPlayer(contentsOf: url)
        audioPlayer?.prepareToPlay()
        audioPlayer?.delegate = self
        audioPlayer?.play()
        let percentage = (audioPlayer?.currentTime ?? 0)/(audioPlayer?.duration ?? 0)
        DispatchQueue.main.async {
            // do what ever you want with that "percentage"
        }

    } catch let error {
        audioPlayer = nil
    }

}
like image 186
Abdo Avatar answered Oct 20 '22 09:10

Abdo