Adrienne
Adrienne

Reputation: 2680

How to download a .zip file in Swift without NSURLSession?

I'm trying to download a zip file to the user's phone storage in an iOS app. Is it possible to do this without NSURLSession?

Upvotes: 0

Views: 780

Answers (1)

Matic Oblak
Matic Oblak

Reputation: 16774

Yes, there are multiple tools but you should still try and use URL session.

A very easy way to do this is using Data. But it blocks your thread so you need to work a bit with queues to make it work properly (Otherwise your app MAY crash).

A very simple, non-safe, thread-blocking way would be:

func saveFile(atRemoteURL remoteURL: URL, to localURL: URL) {
    let data = try! Data(contentsOf: remoteURL)
    try! data.write(to: localURL)
}

But doing it a bit more stable should look something like this:

private func downloadIteam(atURL url: URL, completion: ((_ data: Data?, _ error: Error?) -> Void)?) {
    let queue = DispatchQueue(label: "downloading_file")
    queue.async {
        do {
            let data = try Data(contentsOf: url)
            completion?(data, nil)
        } catch {
            completion?(nil, error)
        }
    }
}

private func saveDataToDocuments(_ data: Data, to: String, completion: ((_ resultPath: URL?, _ error: Error?) -> Void)?) {
    let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent(to)
    let queue = DispatchQueue(label: "saving_file")
    queue.async {
        do {
            let folderPath: String = path.deletingLastPathComponent().path
            if !FileManager.default.fileExists(atPath: folderPath) {
                try FileManager.default.createDirectory(atPath: folderPath, withIntermediateDirectories: true, attributes: nil)
            }
            try data.write(to: path)
            completion?(path, nil)
        } catch {
            completion?(nil, error)
        }
    }
}

public func downloadFileAndSaveItToDocuments(urlToRemoteFile: URL, completion: @escaping (_ file: (relativePath: String, fullPath: URL)?, _ error: Error?) -> Void) {
    
    let fileName = urlToRemoteFile.lastPathComponent
    let relativePath = "downloads/" + fileName
    
    func finish(fullPath: URL?, error: Error?) {
        DispatchQueue.main.async {
            if let path = fullPath {
                completion((relativePath, path), error)
            } else {
                completion(nil, error)
            }
        }
    }
    
    downloadIteam(atURL: urlToRemoteFile) { (data, error) in
        guard let data = data else {
            completion(nil, error)
            return
        }
        saveDataToDocuments(data, to: relativePath) { (url, saveError) in
            finish(fullPath: url, error: saveError ?? error)
        }
    }
}

I hope the code is self-documented enough.

Upvotes: 1

Related Questions