【问题标题】:Cannot seem to fetch cached response with Alamofire似乎无法使用 Alamofire 获取缓存的响应
【发布时间】:2021-09-22 03:41:18
【问题描述】:

我正在尝试在 Alamofire 的 ResponseCacher 的帮助下缓存 JSON 响应 (https://api.github.com/search/repositories),在客户端中的有效期为 2 小时。在one of the posts in SO,Alamofire 的 Jon 建议修改 ResponseCacher 中的 CachedURLResponse。响应没有 Cache-Control 或任何缓存标头。所以我在响应中手动添加了缓存控制标头。根据我的理解,我还整理了会话配置代码,但是我似乎无法让缓存与覆盖的到期时间一起工作。会话定义如下:

let sessionConfiguration = URLSessionConfiguration.af.default
sessionConfiguration.requestCachePolicy = .useProtocolCachePolicy
sessionConfiguration.urlCache = URLCache(memoryCapacity: 20 * 1024 * 1024, diskCapacity: 100 * 1024 * 1024)

let responseCacher = ResponseCacher(behavior: .modify { _, response in
  return CachedURLResponse(
    response: response.response,
    data: response.data,
    userInfo: userInfo,
    storagePolicy: .allowed)
    .response(with: 7200)
})

let aSession = Session(configuration: sessionConfiguration, cachedResponseHandler: responseCacher)

其中响应​​修饰符定义如下

extension CachedURLResponse {
    func response(with expirationDuration: Int) -> CachedURLResponse {
        var cachedResponse = self
        if let httpResponse = cachedResponse.response as? HTTPURLResponse, var headers = httpResponse.allHeaderFields as? [String : String], let url = httpResponse.url{

            headers["Cache-Control"] = "private, max-age=\(expirationDuration)"
            headers.removeValue(forKey: "Expires")
            headers.removeValue(forKey: "s-maxage")

            if let newResponse = HTTPURLResponse(url: url, statusCode: httpResponse.statusCode, httpVersion: "HTTP/1.1", headerFields: headers) {
            cachedResponse = CachedURLResponse(response: newResponse, data: cachedResponse.data, userInfo: headers, storagePolicy: cachedResponse.storagePolicy)
            }
        }
        return cachedResponse
    }
}

如果我将 requestCachePolicy 设置为.useProtocolCachePolicy,当网络离线时,我不会得到任何缓存响应。如果我将 requestCachePolicy 设置为.returnCacheDataElseLoad,则缓存永不过期。我需要缓存我的响应,但也要让它在 2 小时后过期,并在网络离线时返回缓存的响应。

我是缓存方面的初学者,所以有些地方我在没有完全理解它是如何工作的情况下取得了一些飞跃。部分代码取自 SO。

【问题讨论】:

    标签: ios caching alamofire


    【解决方案1】:

    处理缓存的第一件事是 URLCache。您需要覆盖默认的 URLCache 并通过添加缓存控制标头来强制缓存。

    这样的事情应该可以工作:

    let sessionConfiguration = URLSessionConfiguration.af.default
    sessionConfiguration.urlCache = ForceCache.makeForceCache()
    
    let aSession = Session(configuration: sessionConfiguration)
    
    /// The server does not return proper cache control headers for caching so we override it here
    private class ForceCache: URLCache {
    
        static func makeForceCache() -> URLCache {
            let memoryCapacity = 20 * 1_024 * 1_024
            // According to https://developer.apple.com/documentation/foundation/nsurlsessiondatadelegate/1411612-urlsession?language=objc
            // The response size is small enough to reasonably fit within the cache.
            // (For example, if you provide a disk cache, the response must be no larger than
            // about 5% of the disk cache size.)
            let diskCapacity = 400 * 1_024 * 1_024
            let path = "org.something.something"
    
            return ForceCache(memoryCapacity: memoryCapacity,
                              diskCapacity: diskCapacity,
                              diskPath: path)
        }
    
        private let constSecondsToKeepOnDisk = 2 * 60 * 60 // 2 hours
    
        override func storeCachedResponse(_ cachedResponse: CachedURLResponse, for request: URLRequest) {
            var customCachedResponse = cachedResponse
            // Set custom Cache-Control
            if let response = cachedResponse.response as? HTTPURLResponse,
               var newHeaders = response.allHeaderFields as? [String: String] {
                newHeaders["Cache-Control"] = "public, max-age=\(constSecondsToKeepOnDisk)"
                newHeaders["Expires"] = nil
                newHeaders["s-maxage"] = nil
    
                if let url = response.url,
                   // Consider only specific request
                   url.absoluteString.contains("search/repositories"),
                   let newResponse = HTTPURLResponse(url: url,
                                                     statusCode: response.statusCode,
                                                     httpVersion: "HTTP/1.1", headerFields: newHeaders) {
                    customCachedResponse = CachedURLResponse(response: newResponse,
                                                             data: cachedResponse.data,
                                                             userInfo: cachedResponse.userInfo,
                                                             storagePolicy: cachedResponse.storagePolicy)
                }
            }
            super.storeCachedResponse(customCachedResponse, for: request)
        }
    }
    

    为了完整起见,我应该补充一点,通常缓存应该由服务器处理。如果服务器发送您不应该缓存请求,这意味着该决定背后有充分的理由(或设计错误)。

    【讨论】:

    • 这对你有用吗?因为当我断开网络时没有返回缓存的 JSON。
    猜你喜欢
    • 2014-07-05
    • 1970-01-01
    • 1970-01-01
    • 2020-01-06
    • 1970-01-01
    • 2016-12-08
    • 2018-05-21
    • 1970-01-01
    • 2023-03-13
    相关资源
    最近更新 更多