Last active
January 30, 2019 17:55
-
-
Save robertmryan/fd52d2dcc4cdbb8632d1bf59f598a342 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// FileDownloader.swift | |
// | |
// Created by Robert Ryan on 1/30/19. | |
// | |
import Foundation | |
import os.log | |
private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "FileDownloader") | |
class FileDownloader { | |
private static let downloadsSession = URLSession(configuration: .default) | |
private weak var task: URLSessionDownloadTask? | |
private let url: URL | |
init(url: URL) { | |
self.url = url | |
} | |
/// Start download | |
public func startDownload() { | |
let task = FileDownloader.downloadsSession.downloadTask(with: url) { location, response, error in | |
self.saveDownload(originalURL: self.url, at: location, response: response, error: error) | |
} | |
task.resume() | |
self.task = task | |
} | |
/// Cancel download | |
func cancel() { | |
task?.cancel() | |
} | |
} | |
private extension FileDownloader { | |
/// Once download is done, save the resulting file if it was successful. | |
/// | |
/// This will save it to the "documents" | |
/// | |
/// - Parameters: | |
/// - originalURL: The original URL of the download request. | |
/// - location: The temporary location of the the downloaded file to be saved. | |
/// - response: The `URLResponse` of the download request. | |
/// - error: The `Error` of the download request. | |
func saveDownload(originalURL: URL, at location: URL?, response: URLResponse?, error: Error?) { | |
guard error == nil, let location = location else { | |
os_log("error %{PUBLIC}@", log: log, type: .error, error?.localizedDescription ?? "Unknown error") | |
return | |
} | |
let destination = localCachesURL(for: originalURL) | |
let fileManager = FileManager.default | |
try? fileManager.removeItem(at: destination) | |
do { | |
try fileManager.copyItem(at: location, to: destination) | |
os_log("Save was completed at %{PUBLIC}@ from %{PUBLIC}@", log: log, type: .debug, destination.absoluteString, originalURL.absoluteString) | |
} catch let error { | |
os_log("Could not copy file to disk: %{PUBLIC}@", log: log, type: .error, error.localizedDescription) | |
} | |
} | |
/// The local directory to save the file. | |
/// | |
/// - Parameter url: The URL of the original request. | |
/// | |
/// - Returns: The URL in the local file system | |
func localCachesURL(for url: URL) -> URL { | |
return try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) | |
.appendingPathComponent(url.lastPathComponent) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This includes a few changes:
Use
static
forURLSession
so that we’re not instantiating a new one for every download.URLSession
has some overhead, so you really only want one for all of your downloads.Make
task
propertyweak
. We don’t need to hang on it it after the download is done. Obviously, instartDownload
that means that we need to temporarily save the task to a local var,resume
it, and only then set the ivar. But this ensures that when the task is completed, it is released. Not critical, but best practice.You were saving to the downloads folder. Apple advises that this folder is (a) only for files that are user documents; and (b) cannot be easily re-retrieved. Downloads are usually saved to caches folder.
I would not throw assertion if the download failed. E.g. do you really want app to crash if the web server is temporarily unavailable?
The only reason you’d save the
task
to an ivar is if you were going to reference it later. So I added typical use-case, e.g. acancel
method.