Using URLCache with download tasks (NSURLCache & NSURLSessionDownloadTask)
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
import Foundation | |
import os.log | |
class URLCacheTest { | |
let logger = Logger(subsystem: "URLCacheTest", category: "main") | |
// HTTP HEADERS: | |
// Date: Wed, 04 Nov 2020 11:13:24 GMT | |
// Server: Apache | |
// Strict-Transport-Security: max-age=63072000; includeSubdomains; preload | |
// X-Content-Type-Options: nosniff | |
// X-Frame-Options: SAMEORIGIN | |
// Last-Modified: Sun, 19 May 2002 14:49:00 GMT | |
// Accept-Ranges: bytes | |
// Content-Length: 20702285 | |
// Content-Type: application/pdf | |
let flightPlanURL = URL(string: "https://www.hq.nasa.gov/alsj/a17/A17_FlightPlan.pdf")! | |
// Custom URL cache with 1 GB disk storage | |
lazy var cache: URLCache = { | |
let cachesURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0] | |
let diskCacheURL = cachesURL.appendingPathComponent("DownloadCache") | |
let cache = URLCache(memoryCapacity: 100_000_000, diskCapacity: 1_000_000_000, directory: diskCacheURL) | |
logger.info("Cache path: \(diskCacheURL.path)") | |
return cache | |
}() | |
// Custom URLSession that uses our cache | |
lazy var session: URLSession = { | |
let config = URLSessionConfiguration.default | |
config.urlCache = cache | |
return URLSession(configuration: config) | |
}() | |
init() { | |
let documentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] | |
let targetURL = documentURL.appendingPathComponent(flightPlanURL.lastPathComponent) | |
downloadFile(remoteURL: flightPlanURL, targetURL: targetURL) | |
} | |
func downloadFile(remoteURL: URL, targetURL: URL) { | |
let request = URLRequest(url: remoteURL) | |
let downloadTask = session.downloadTask(with: request) { url, response, error in | |
self.logger.info("Download Task complete") | |
// Store data in cache | |
if let response = response, let url = url, | |
self.cache.cachedResponse(for: request) == nil, | |
let data = try? Data(contentsOf: url, options: [.mappedIfSafe]) { | |
self.cache.storeCachedResponse(CachedURLResponse(response: response, data: data), for: request) | |
} | |
// Move file to target location | |
guard let tempURL = url else { return } | |
_ = try? FileManager.default.replaceItemAt(targetURL, withItemAt: tempURL) | |
} | |
downloadTask.resume() | |
} | |
} |
A small side note, using URLSession with background configuration will ignore the URLCache object, its a "minor" not well documented fact. I had it confirmed with a ATS a few years back as far as i know its still there, and very poorly documented
Indeed. I mentioned this in the blog post.
Thanks for this excellent article and accompanying gist!
Hmm doesn’t this defeat the main benefit of a download task, which is to avoid loading the entire file contents into memory?
The data is memory mapped, it is not actually loaded into memory. This approach works even for multi-GB-files.
Oooh whoops missed the .mappedIfSafe
. Thanks, @steipete!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://pspdfkit.com/blog/2020/downloading-large-files-with-urlsession/