使用Alamofire的ResponseCacher无法按2小时有效期获取缓存响应
问题原因
- 你当前代码存在三个核心问题:
- 初始化
CachedURLResponse时传入的userInfo变量未定义,会导致运行时错误,修改后的缓存响应无法正常写入缓存 - 系统默认的
.useProtocolCachePolicy策略在离线场景下,会尝试向服务端验证缓存有效性,验证失败就不会返回缓存内容 - 仅设置
Cache-Control头兼容性不足,部分系统版本的URLCache会优先读取Expires头判断缓存有效性,缺失该头会导致缓存过期逻辑失效
- 初始化
修正方案
1. 修正缓存响应构造扩展
extension CachedURLResponse { func response(with expirationDuration: TimeInterval) -> CachedURLResponse { guard let httpResponse = response as? HTTPURLResponse, let url = httpResponse.url, var headers = httpResponse.allHeaderFields as? [String: String] else { return self } // 同时设置Cache-Control和Expires,兼容不同系统版本的缓存判断 headers["Cache-Control"] = "max-age=\(expirationDuration), private" headers.removeValue(forKey: "s-maxage") // 生成Expires头:当前时间加有效期,转成标准RFC1123格式 let expirationDate = Date().addingTimeInterval(expirationDuration) let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US_POSIX") dateFormatter.timeZone = TimeZone(secondsFromGMT: 0) dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss zzz" headers["Expires"] = dateFormatter.string(from: expirationDate) // 把过期时间存到userInfo里,方便后续自定义判断 var userInfo = self.userInfo ?? [:] userInfo["customExpirationDate"] = expirationDate guard let newHttpResponse = HTTPURLResponse( url: url, statusCode: httpResponse.statusCode, httpVersion: "HTTP/1.1", headerFields: headers ) else { return self } return CachedURLResponse( response: newHttpResponse, data: data, userInfo: userInfo, storagePolicy: .allowed ) } }
2. 修正会话初始化代码
let sessionConfiguration = URLSessionConfiguration.af.default // 全局默认用协议缓存策略,在线场景下生效 sessionConfiguration.requestCachePolicy = .useProtocolCachePolicy sessionConfiguration.urlCache = URLCache( memoryCapacity: 20 * 1024 * 1024, // 20MB内存缓存 diskCapacity: 100 * 1024 * 1024 // 100MB磁盘缓存 ) let responseCacher = ResponseCacher(behavior: .modify { _, response in return CachedURLResponse( response: response.response, data: response.data, userInfo: nil, storagePolicy: .allowed ).response(with: 7200) // 2小时有效期 })
3. 添加请求拦截器适配离线场景
通过拦截器动态调整缓存策略,离线时直接返回缓存内容:
// 定义请求拦截器 class CacheInterceptor: RequestInterceptor { func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) { var request = urlRequest // 判断当前网络是否可用,可直接使用Alamofire自带的网络状态监听能力 if !(NetworkReachabilityManager.default?.isReachable ?? false) { // 离线状态下直接返回缓存,不校验有效期 request.cachePolicy = .returnCacheDataDontLoad } else { // 在线状态下遵守协议缓存策略,自动判断缓存是否超过2小时有效期 request.cachePolicy = .useProtocolCachePolicy } completion(.success(request)) } } // 初始化会话时传入拦截器 let aSession = Session( configuration: sessionConfiguration, interceptor: CacheInterceptor(), cachedResponseHandler: responseCacher )
注意事项
- 仅GET请求会被系统默认缓存,POST等其他请求需要额外处理缓存逻辑
- 首次请求成功后响应才会写入缓存,后续请求才会命中缓存
- 确保
URLCache容量足够,超出容量的缓存会被系统自动清理
内容的提问来源于stack exchange,提问作者Aswath
相关产品推荐
相关产品推荐

