URLCache (iOS).storeCachedResponse работает асинхронно.Как поймать завершение? - PullRequest
0 голосов
/ 05 декабря 2018

Только что обнаружил, что функция storeCachedResponse (_cachedResponse: CachedURLResponse, для 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?

Я собираюсь использовать кэшированные файлы сразу после их создания.Более того, я собираюсь работать с кэшированными файлами напрямую, и это не лучшее решение для установки задержки.

1 Ответ

0 голосов
/ 29 апреля 2019

На самом деле я не мог понять, почему вы вызываете кэшированные данные сразу после кэширования !?По моему мнению, вы должны вызывать кэшированные данные перед запросом URL с сеансом, если данные существуют, возвращать кэшированные данные, в противном случае запрос с нуля.

Например:

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)
            }
 })

При первом получении данных из Интернета и при втором запросе он немедленно вернет кэшированные данные.

...