Цель
Я пытаюсь вставить данные / ответ из URLRequest в еще один URLRequest в моем кэше.
Настройка
Это простообразец кода.Это готово быть сброшенным в проект.
Я пытаюсь использовать ответ + данные, полученные из моего landscapeURLString
сетевого запроса ... сохранить в кеш моего сеанса для моего запроса lizardURLString .
import UIKit
class ViewController: UIViewController {
lazy var defaultSession : URLSession = {
let urlCache = URLCache(memoryCapacity: 500 * 1024 * 1024, diskCapacity: 500 * 1024 * 1024, diskPath: "something")
let configuration = URLSessionConfiguration.default
configuration.urlCache = urlCache
let session = URLSession(configuration: configuration)
return session
}()
lazy var downloadLizzardbutton : UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("download lizard image OFFLINE", for: .normal)
btn.backgroundColor = .blue
btn.addTarget(self, action: #selector(downloadLizardAction), for: .touchUpInside)
return btn
}()
let imageView : UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
// I make sure my internet is set to OFF so that it forces this to be read from cache...
@objc func downloadLizardAction() {
downloadImage(from: lizardURLString, from: defaultSession)
}
let lizardURLString = "https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg"
let landscapeURLString = "https://images.pexels.com/photos/414171/pexels-photo-414171.jpeg"
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(imageView)
view.addSubview(downloadLizzardbutton)
imageView.pinToAllEdges(of: view)
downloadImage(from: landscapeURLString, from: defaultSession)
}
private func downloadImage(from urlString: String, from session : URLSession){
guard let url = URL(string: urlString) else{
fatalError("bad String we got!")
}
let urlRequest = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 15)
print("url.hashValue: \(urlRequest.hashValue)")
let task = session.dataTask(with: urlRequest) { [weak self] (data, response, error) in
guard error == nil else {
print(error)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("response NOT 2xx: \(response)")
return
}
for header in httpResponse.allHeaderFields{
if let key = header.key as? String, key == "Cache-Control"{
print("found Cache-Control: \(httpResponse.allHeaderFields["Cache-Control"])")
}
}
if let data = data,
let image = UIImage(data: data){
let lizardURL = URL(string: self!.lizardURLString)
let lizardURLRequest = URLRequest(url: lizardURL!)
let landscapeCachedURLPResponse : CachedURLResponse = CachedURLResponse(response: response!, data: data, userInfo:nil, storagePolicy: .allowed)
print("before storing into cache: \(String(describing: session.configuration.urlCache?.cachedResponse(for: lizardURLRequest)))")
session.configuration.urlCache?.storeCachedResponse(landscapeCachedURLPResponse, for: lizardURLRequest)
print("after storing into cache: \(String(describing: session.configuration.urlCache?.cachedResponse(for: lizardURLRequest)))")
print("lizardRequest.hashValue: \(lizardURLRequest.hashValue)")
DispatchQueue.main.async {
self?.imageView.image = image
}
}
}
task.resume()
}
}
extension UIView{
func pinToAllEdges(of view: UIView){
let leading = leadingAnchor.constraint(equalTo: view.leadingAnchor)
let top = topAnchor.constraint(equalTo: view.topAnchor)
let trailing = trailingAnchor.constraint(equalTo: view.trailingAnchor)
let bottom = bottomAnchor.constraint(equalTo: view.bottomAnchor)
NSLayoutConstraint.activate([leading, top, trailing, bottom])
}
}
Вещи, которые я уже проверил:
- Мой
landscapeURLString
имеет заголовок cache-control
с max-age
из 31536000
- Если - это новая установка, то перед сохранением в кеш, мой
cachedResponse
для lizardURLString равен nil
.Но после хранения больше не nil
.В результате я делаю вывод, что я успешно что-то храню в кеше! - Я также подозреваю, что URLCache рассматривает URLRequest в качестве ключа.Поэтому я напечатал hashValue моего
lizardURLString
.Это то же самое, что ключ, который я сохранил.Комбинируя это с вышеприведенным пунктом, я пришел к выводу, что в кеше существует точный ключ! - Я также вижу, что когда я сохраняю его в кеше, мой
currentMemoryUsage
увеличивается.
Как я тестирую и что вижу:
- Я просто загружаю изображение ландшафта.
- Выключить мой интернет
- Нажмите кнопку, чтобы загрузить изображение ящерицы.
Очевидно, что он не в сети.Я ожидаю, что он будет использовать из кэша, но это не так.Все, что я получаю, это время!
Я также пытался изменить cachePolicy
на returnCacheDataElseLoad
, но это не помогло либо
EDIT1:
Я также попытался сделать то, что сказал и сделал Дэвид:
let landscapeHTTPResponse : HTTPURLResponse = HTTPURLResponse(url: self!.lizardURL, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: (httpResponse.allHeaderFields as! [String : String]))!
let landscapedCachedURLPResponse : CachedURLResponse = CachedURLResponse(response: landscapeHTTPResponse, data: data, userInfo:nil, storagePolicy: .allowed)
и сохраненный landscapedCachedURLPResponse
в кэш.Это тоже не сработало.Это также истекает - не все заглядывают в кеш.
EDIT2:
Так что я добился определенного прогресса.Или, возможно, сделал один шаг назад и один шаг вперед.
Я попытался проверить, смогу ли я сохранить ответ для того же URL-адреса и посмотреть, смогу ли я получить ответ после очистки кеша.Я не смог.
Я создавал свой кэшированный ответ примерно так:
let cachedResponse = CachedURLResponse(response: response!, data: data, userInfo:nil, storagePolicy: .allowed)
или просто так:
let cachedResponse = CachedURLResponse(response: response!, data: data)
Что заставило эту часть работать?:
let cachedResponseFromCache = session.configuration.urlCache?.cachedResponse(for: self!.landscapeURLRequest)
self._cachedResponse = cachedResponseFromCache
Тогда я:
- очистил кеш
- отключил интернет
- попытался загрузить изображение, но безуспешнои это хорошо.Это ожидаемое поведение
- , сохраняющее
cachedResponseFromCache
свойство в кеше. - удалось извлечь из кеша *
Я не уверен, в чем разница междувытащить из самого кеша и создать кеш из Response + Data
.
Это важно, потому что я начал задавать вопрос, есть ли еще какие-либо формы внутренних ошибок в URLCache .Это дало мне основание полагать, что оно может работать как положено.
Теперь я знаю, как работает процесс сохранения в кеш.Я знаю, что мой URL-ответ хорош.Мне просто нужно пройти через отображение URLRequest
EDIT3:
Гай Когус предположил, что мои URL должны быть из одного источника.Поэтому, как только я скачал упомянутое им изображение медведя, мое изображение ящерицы проходило.VOILA!
Как очень важное замечание по отладке, которое я узнал: даже если вы добились успеха в какой-то части (то, что она кешировала ландшафтное изображение для себя) проблемы, изменение переменных (здесь изменение исходного URL) можетвсегда меняйте все результаты тестирования.
Он подозревал, что это потому, что заголовок Server
в общем ресурсе, и это важно для поиска cachedResponse.
Я опроверг это утверждение, сказав, что мой lizardURLRequest сделан, когда он находится в сети, поэтому сравнивать его не с чем, но он работает!Поэтому следующая идея заключалась в том, что он может иметь отношение к некоторой части URL, например, к первому сегменту или к чему-то еще.
Итак, я пошел и изменил lizardURL из:
https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg
примерно так: https://skdhfsupload.qwiklkjlkjimedia.com/qwikipehkjdia/eeeeeecommons/sdalfjkdse/aldskfjae0/extraParam/anotherextraparam/asasdLarge_Scaled_Forest_Lizard.jpeg
Я добавил немые символы в URL.Я также добавил дополнительные сегменты в него.Я изменил тип файла в конце.
Все еще это работало.Таким образом, единственное, что я могу сделать вывод, это то, что что-то из Заголовков принимает решения
Заголовки для моего landscapeURL : (кэширование для другого URL не работает)
Content-Length : 997361
x-cache : HIT, MISS
cf-ray : 472793e93ce39574-IAD
x-served-by : cache-lax8621-LAX, cache-iad2132-IAD
cf-cache-status : HIT
Last-Modified : Sun, 14 Oct 2018 2:10:05 GMT
Accept-Ranges : bytes
Vary : Accept-Encoding
x-content-type-options : nosniff
Content-Type : image/jpeg
expect-ct : max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Set-Cookie : __cfduid=d5f5fd59ce5ff9ac86e42f8c008708ae61541004176; expires=Thu, 31-Oct-19 16:42:56 GMT; path=/; domain=.pexels.com; HttpOnly
Expires : Thu, 31 Oct 2019 16:42:56 GMT
Server : cloudflare
Cache-Control : public, max-age=31536000
Date : Wed, 31 Oct 2018 16:42:56 GMT
заголовки для моего BearURL : (для этого работает кэширование для другого URL)
Date : Wed, 31 Oct 2018 16:46:38 GMT
Content-Length : 215104
x-client-ip : 2001:558:1400:4e:808c:2738:43e:36f5
access-control-expose-headers : Age, Date, Content-Length, Content-Range, X-Content-Duration, X-Cache, X-Varnish
x-cache : cp1076 miss, cp1088 hit/21
Age : 27646
Etag : 00e21950bf432476c91b811bb685b6af
Strict-Transport-Security : max-age=106384710; includeSubDomains; preload
x-analytics : https=1;nocookies=1
Accept-Ranges : bytes
x-object-meta-sha1base36 : 42tq5grg9rq1ydmqd4z5hmmqj6h2309
x-varnish : 48388488, 503119619 458396839
x-cache-status : hit-front
Content-Type : image/jpeg
x-trans-id : tx08ed43bbcc1946269a9a3-005bd97070
Last-Modified : Fri, 04 Oct 2013 23:30:08 GMT
Access-Control-Allow-Origin : *
timing-allow-origin : *
x-timestamp : 1380929407.39127
Via : 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)
Важное примечание:
Для BearURL - кэширование для BearURL и lizardURL или любой другой URL работает.Для landscapeURL кэширование работает только для самого landscapeURL.Он не работает для любого другого URL.
Итак, текущее состояние вопроса таково: какие заголовки необходимо включить, чтобы это работало?