matteoh
matteoh

Reputation: 3620

How to save a downloading file at a specific location so I can read it while downloading in iOS?

In my iOS project, I need to download a file at a specific location on the device, so I can start reading it while it's still being downloaded.

My goal here is to stream an audio file from the start, but I need that audio file to be saved locally at its final location (I don't want the file to be saved in a temporary folder during the download, and then moved to its final location when the download is complete, which apparently is the default behavior).

So here is what I've tried so far:


private var observation: NSKeyValueObservation?

func downloadAudioFile()
{
    let url: URL = URL(string: "https://www.demo.com/audiofile.ogg")!
    let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
        if (error != nil)
        {
            print(error)
        }
        else
        {
            print("download complete")
        }
    }
    
    observation = task.progress.observe(\.fractionCompleted) {(progress, _) in
        print("progression : \(progress)")
    }
    
    task.resume()
}

But since I'm quite a beginner in iOS, I don't know how to save the downloaded data while it's downloading.

How can I achieve that?

Thanks.

Upvotes: 2

Views: 5385

Answers (1)

matteoh
matteoh

Reputation: 3620

OK so here is what I did (it might not be the cleanest solution, but it works):

class Downloader : NSObject, URLSessionDelegate, URLSessionDataDelegate
{
    private var session: URLSession!
    private var dataTask: URLSessionDataTask!
    private var fileUrl: URL!

    func download(url: String, fileName: String)
    {
        // get path of directory
        guard let directory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else {
            return
        }
        
        // create file url
        fileUrl = directory.appendingPathComponent(fileName)
        
        // starts download
        session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)
        var request: URLRequest = try! URLRequest(url: URL(string: url)!)
        request.cachePolicy = .reloadIgnoringLocalCacheData
        dataTask = session.dataTask(with: request)
        dataTask.resume()
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data)
    {
        writeToFile(data: data)
    }

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse,
                    completionHandler: (URLSession.ResponseDisposition) -> Void)
    {
        completionHandler(URLSession.ResponseDisposition.allow)
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?)
    {
        if (error == nil)
        {
            // download complete without any error
        }
        else
        {
            // error during download
        }
    }

    func writeToFile(data: Data)
    {
        // if file exists then write data
        if FileManager.default.fileExists(atPath: fileUrl.path)
        {
            if let fileHandle = FileHandle(forWritingAtPath: fileUrl.path)
            {
                // seekToEndOfFile, writes data at the last of file(appends not override)
                fileHandle.seekToEndOfFile()
                fileHandle.write(data)
                fileHandle.closeFile()
            }
            else
            {
                // Can't open file to write
            }
        }
        else
        {
            // if file does not exist write data for the first time
            do
            {
                try data.write(to: fileUrl, options: .atomic)
            }
            catch
            {
                // Unable to write in new file
            }
        }
    }
}

Upvotes: 2

Related Questions