URLCache(iOS)。 storeCachedResponse 异步工作。如何赶上完成?
URLCache (iOS). storeCachedResponse works asynchronously. How to catch the completion?
刚刚发现函数 storeCachedResponse(_ cachedResponse: CachedURLResponse, for request: URLRequest) 异步工作。即执行后不会立即返回结果。我没有在官方文档中找到对此的描述。
参见示例:
cache = URLCache(memoryCapacity: 0, diskCapacity: 100 * 1024 * 1024, diskPath: "myCache")
let config = URLSessionConfiguration.default
config.requestCachePolicy = .returnCacheDataElseLoad
config.urlCache = cache
let session = URLSession(configuration: config)
session.dataTask(with: request, completionHandler: {[unowned self]
(data, response, error) in
if let data = data, let response = response, ((response as HTTPURLResponse)?.statusCode ?? 500) < 300 {
let cachedData = CachedURLResponse(response: response, data: data)
self.cache.storeCachedResponse(cachedData, for: request)
let testCachedData = self.cache.cachedResponse(for: request)
}
}
理论上 testCachedData 必须包含缓存的响应。但它实际包含的内容:
testCachedData?.response.url // Ok
testCachedData?.isEmpty // false
testCachedData?.data // 0 bytes!!!
虽然testCachedData?.data说它包含0字节,但是我们可以把这个数据写到一个文件中,这个文件包含的是真正的数据,而不是0。如果我们深入本地缓存目录(~/Library/Caches/myApp/MyCache ) 在 cachedResponse 调用后立即在断点处暂停时,我们可以看到包含缓存文件 (fsCachedData) 的文件夹尚不存在。
现在让我们在 storeCachedResponse 和 cachedResponse 之间插入延迟:
cache = URLCache(memoryCapacity: 0, diskCapacity: 100 * 1024 * 1024, diskPath: "myCache")
let config = URLSessionConfiguration.default
config.requestCachePolicy = .returnCacheDataElseLoad
config.urlCache = cache
let session = URLSession(configuration: config)
session.dataTask(with: request, completionHandler: {[unowned self]
(data, response, error) in
if let data = data, let response = response, ((response as HTTPURLResponse)?.statusCode ?? 500) < 300 {
let cachedData = CachedURLResponse(response: response, data: data)
self.cache.storeCachedResponse(cachedData, for: request)
delay(5) // JUST 5 SEC DELAY
let testCachedData = self.cache.cachedResponse(for: request)
}
}
现在:
testCachedData?.response.url // Ok
testCachedData?.isEmpty // false
testCachedData?.data // contains bytes
因此,在延迟 5 秒后,我们看到缓存文件文件夹 (fsCachedData) 存在并且包含缓存文件(例如 D8A30D21-C8F1-4FCA-967E-F6B440998173)。
重点是如何捕捉storeCachedResponse的完成?
我将在创建缓存文件后立即使用它们。而且,我打算直接处理缓存文件,设置延迟不是最好的解决方案。
其实我一直不明白你为什么缓存后马上调用缓存的数据!?在我看来,如果数据存在,你应该在请求 url 之前调用缓存数据 return 缓存数据,否则从头开始请求。
例如:
private let allowedDiskSize = 100 * 1024 * 1024
private lazy var cache: URLCache = {
return URLCache(memoryCapacity: 0, diskCapacity: allowedDiskSize, diskPath: "gifCache")
}()
typealias DownloadCompletionHandler = (Result<Data,Error>) -> ()
private func createAndRetrieveURLSession() -> URLSession {
let sessionConfiguration = URLSessionConfiguration.default
sessionConfiguration.requestCachePolicy = .returnCacheDataElseLoad
sessionConfiguration.urlCache = cache
return URLSession(configuration: sessionConfiguration)
}
private func downloadContent(fromUrlString: String, completionHandler: @escaping DownloadCompletionHandler) {
guard let downloadUrl = URL(string: fromUrlString) else { return }
let urlRequest = URLRequest(url: downloadUrl)
// First try to fetching cached data if exist
if let cachedData = self.cache.cachedResponse(for: urlRequest) {
print("Cached data in bytes:", cachedData.data)
completionHandler(.success(cachedData.data))
} else {
// No cached data, download content than cache the data
createAndRetrieveURLSession().dataTask(with: urlRequest) { (data, response, error) in
if let error = error {
completionHandler(.failure(error))
} else {
let cachedData = CachedURLResponse(response: response!, data: data!)
self.cache.storeCachedResponse(cachedData, for: urlRequest)
completionHandler(.success(data!))
}
}.resume()
}
}
和用法:
self.downloadContent(fromUrlString: ANY_URL, completionHandler: { (result) in
switch result {
case .success(let yourData):
// handle data
case .failure(let error):
debugPrint(error.localizedDescription)
}
})
第一次它会从网络上获取数据,在第二次请求中它会立即return缓存数据。
刚刚发现函数 storeCachedResponse(_ cachedResponse: CachedURLResponse, for request: URLRequest) 异步工作。即执行后不会立即返回结果。我没有在官方文档中找到对此的描述。 参见示例:
cache = URLCache(memoryCapacity: 0, diskCapacity: 100 * 1024 * 1024, diskPath: "myCache")
let config = URLSessionConfiguration.default
config.requestCachePolicy = .returnCacheDataElseLoad
config.urlCache = cache
let session = URLSession(configuration: config)
session.dataTask(with: request, completionHandler: {[unowned self]
(data, response, error) in
if let data = data, let response = response, ((response as HTTPURLResponse)?.statusCode ?? 500) < 300 {
let cachedData = CachedURLResponse(response: response, data: data)
self.cache.storeCachedResponse(cachedData, for: request)
let testCachedData = self.cache.cachedResponse(for: request)
}
}
理论上 testCachedData 必须包含缓存的响应。但它实际包含的内容:
testCachedData?.response.url // Ok
testCachedData?.isEmpty // false
testCachedData?.data // 0 bytes!!!
虽然testCachedData?.data说它包含0字节,但是我们可以把这个数据写到一个文件中,这个文件包含的是真正的数据,而不是0。如果我们深入本地缓存目录(~/Library/Caches/myApp/MyCache ) 在 cachedResponse 调用后立即在断点处暂停时,我们可以看到包含缓存文件 (fsCachedData) 的文件夹尚不存在。 现在让我们在 storeCachedResponse 和 cachedResponse 之间插入延迟:
cache = URLCache(memoryCapacity: 0, diskCapacity: 100 * 1024 * 1024, diskPath: "myCache")
let config = URLSessionConfiguration.default
config.requestCachePolicy = .returnCacheDataElseLoad
config.urlCache = cache
let session = URLSession(configuration: config)
session.dataTask(with: request, completionHandler: {[unowned self]
(data, response, error) in
if let data = data, let response = response, ((response as HTTPURLResponse)?.statusCode ?? 500) < 300 {
let cachedData = CachedURLResponse(response: response, data: data)
self.cache.storeCachedResponse(cachedData, for: request)
delay(5) // JUST 5 SEC DELAY
let testCachedData = self.cache.cachedResponse(for: request)
}
}
现在:
testCachedData?.response.url // Ok
testCachedData?.isEmpty // false
testCachedData?.data // contains bytes
因此,在延迟 5 秒后,我们看到缓存文件文件夹 (fsCachedData) 存在并且包含缓存文件(例如 D8A30D21-C8F1-4FCA-967E-F6B440998173)。
重点是如何捕捉storeCachedResponse的完成?
我将在创建缓存文件后立即使用它们。而且,我打算直接处理缓存文件,设置延迟不是最好的解决方案。
其实我一直不明白你为什么缓存后马上调用缓存的数据!?在我看来,如果数据存在,你应该在请求 url 之前调用缓存数据 return 缓存数据,否则从头开始请求。
例如:
private let allowedDiskSize = 100 * 1024 * 1024
private lazy var cache: URLCache = {
return URLCache(memoryCapacity: 0, diskCapacity: allowedDiskSize, diskPath: "gifCache")
}()
typealias DownloadCompletionHandler = (Result<Data,Error>) -> ()
private func createAndRetrieveURLSession() -> URLSession {
let sessionConfiguration = URLSessionConfiguration.default
sessionConfiguration.requestCachePolicy = .returnCacheDataElseLoad
sessionConfiguration.urlCache = cache
return URLSession(configuration: sessionConfiguration)
}
private func downloadContent(fromUrlString: String, completionHandler: @escaping DownloadCompletionHandler) {
guard let downloadUrl = URL(string: fromUrlString) else { return }
let urlRequest = URLRequest(url: downloadUrl)
// First try to fetching cached data if exist
if let cachedData = self.cache.cachedResponse(for: urlRequest) {
print("Cached data in bytes:", cachedData.data)
completionHandler(.success(cachedData.data))
} else {
// No cached data, download content than cache the data
createAndRetrieveURLSession().dataTask(with: urlRequest) { (data, response, error) in
if let error = error {
completionHandler(.failure(error))
} else {
let cachedData = CachedURLResponse(response: response!, data: data!)
self.cache.storeCachedResponse(cachedData, for: urlRequest)
completionHandler(.success(data!))
}
}.resume()
}
}
和用法:
self.downloadContent(fromUrlString: ANY_URL, completionHandler: { (result) in
switch result {
case .success(let yourData):
// handle data
case .failure(let error):
debugPrint(error.localizedDescription)
}
})
第一次它会从网络上获取数据,在第二次请求中它会立即return缓存数据。