Reputation: 2680
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
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