Create downloadUsingCache method — so we don’t download a home page several times in succession, for instance.
This commit is contained in:
parent
d6e436c47f
commit
ca4f4d408f
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue