Create downloadUsingCache method — so we don’t download a home page several times in succession, for instance.

This commit is contained in:
Brent Simmons 2017-11-22 13:37:07 -08:00
parent d6e436c47f
commit ca4f4d408f
1 changed files with 92 additions and 0 deletions

View File

@ -56,3 +56,95 @@ public func download(_ url: URL, _ callback: @escaping OneShotDownloadCallback)
OneShotDownloadManager.shared.download(url, callback) OneShotDownloadManager.shared.download(url, callback)
} }
// MARK: - Downloading using a cache
private struct WebCacheRecord {
let url: URL
let dateDownloaded: Date
let data: Data
let response: URLResponse
func isExpired(_ timeToLive: TimeInterval) -> Bool {
return Date().timeIntervalSince(dateDownloaded) > timeToLive
}
}
private final class WebCache {
private var cache = [URL: WebCacheRecord]()
func cleanup(_ cleanupInterval: TimeInterval) {
cache.keys.forEach { (key) in
let cacheRecord = self[key]!
if shouldDelete(cacheRecord, cleanupInterval) {
self[key] = nil
}
}
}
private func shouldDelete(_ cacheRecord: WebCacheRecord, _ cleanupInterval: TimeInterval) -> Bool {
return Date().timeIntervalSince(cacheRecord.dateDownloaded) > cleanupInterval
}
subscript(_ url: URL) -> WebCacheRecord? {
get {
return cache[url]
}
set {
if let cacheRecord = newValue {
cache[url] = cacheRecord
}
else {
cache[url] = nil
}
}
}
}
private var cache = WebCache()
private let timeToLive: TimeInterval = 5 * 60 // five minutes
private let cleanupInterval: TimeInterval = 20 * 60 // 20 minutes
public func downloadUsingCache(_ url: URL, _ callback: @escaping OneShotDownloadCallback) {
// In the case where a cache record has expired, but the download returned an error,
// we use the cache record anyway. By design.
// Only OK status responses are cached.
cache.cleanup(cleanupInterval)
let cacheRecord: WebCacheRecord? = cache[url]
func callbackWith(_ cacheRecord: WebCacheRecord) {
callback(cacheRecord.data, cacheRecord.response, nil)
}
if let cacheRecord = cacheRecord, !cacheRecord.isExpired(timeToLive) {
callbackWith(cacheRecord)
return
}
download(url) { (data, response, error) in
if let error = error {
if let cacheRecord = cacheRecord {
callbackWith(cacheRecord)
return
}
callback(data, response, error)
return
}
if let data = data, let response = response, response.statusIsOK, error == nil {
let cacheRecord = WebCacheRecord(url: url, dateDownloaded: Date(), data: data, response: response)
cache[url] = cacheRecord
}
callback(data, response, error)
}
}