Swift中使用URLCache缓存API响应及请求失效问题排查
问题分析与修复方案
你的GET API已配置合规的缓存响应头,但iOS客户端仍无法正常缓存响应,结合服务器响应头和客户端代码,以下是核心问题和修复步骤:
1. 错误的缓存目录路径
你的CacheManager将缓存目录设置在了Documents目录下,而iOS中URLCache的标准缓存路径应为Library/Caches目录。Documents目录用于存储用户可直接访问的数据,系统对该目录的缓存行为有特殊限制,可能导致URLCache无法正常读写缓存。
修复代码:
class CacheManager { static let shared = CacheManager() let cache: URLCache let session: URLSession private init() { // 使用Library/Caches下的子目录存储缓存 let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! .appendingPathComponent("api-cache") cache = URLCache(memoryCapacity: 16 * 1024 * 1024, diskCapacity: 80 * 1024 * 1024, diskPath: cacheDir.path) let configuration = URLSessionConfiguration.default configuration.urlCache = cache configuration.requestCachePolicy = .returnCacheDataElseLoad session = URLSession(configuration: configuration) } }
2. 重复创建URLSession实例
每次调用fetch函数时都新建URLSession实例,不仅浪费资源,还可能导致缓存逻辑不一致。建议复用同一个URLSession实例。
修复代码(整合到上述CacheManager):
在CacheManager中提前初始化并复用URLSession,避免每次请求新建。
3. 手动缓存的冗余操作
你手动调用storeCachedResponse存储缓存,但URLSession会根据请求缓存策略和服务器响应头自动处理缓存逻辑,手动存储可能干扰系统的自动缓存机制,导致缓存无法被正确读取。
修复:
移除手动存储缓存的代码,让URLSession遵循HTTP缓存规则自动处理缓存。
4. 缓存命中后的逻辑缺失
原代码中缓存命中后仅打印日志,未调用completion返回缓存数据,导致调用方无法感知缓存命中,误以为缓存未生效。
修复后的fetch函数:
func fetch(filters:[String:Any] = [:], completion: @escaping (Result<[Model], Error>) -> Void) { guard let url = URL(string: apiURL) else { completion(.failure(NSError(domain: "APIError", code: -2, userInfo: [NSLocalizedDescriptionKey: "Invalid API URL"]))) return } var request = URLRequest(url: url, cachePolicy: .returnCacheDataElseLoad) request.httpMethod = "GET" request.timeoutInterval = 60 // 检查缓存命中 if let cachedResponse = CacheManager.shared.cache.cachedResponse(for: request) { print("Cache hit! Data size: \(cachedResponse.data.count) bytes") // 解析缓存数据并回调 do { let model = try JSONDecoder().decode(Model.self, from: cachedResponse.data) completion(.success(model.Model)) } catch { completion(.failure(error)) } return } // 无缓存时发起请求 CacheManager.shared.session.dataTask(with: request) { (data, response, error) in DispatchQueue.main.async { if let error = error { completion(.failure(error)) return } guard let data = data, let response = response as? HTTPURLResponse else { completion(.failure(NSError(domain: "APIError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid data or response"]))) return } if response.statusCode != 200 { completion(.failure(NSError(domain: "APIError", code: response.statusCode, userInfo: [NSLocalizedDescriptionKey: "API error: \(response.statusCode)"]))) return } do { let model = try JSONDecoder().decode(Model.self, from: data) completion(.success(model.Model)) } catch { completion(.failure(error)) } } }.resume() }
额外验证点
- 确认
apiURL每次请求完全一致,动态参数需正确包含在URL中,确保URLCache能匹配缓存键。 - 检查设备存储空间,若磁盘不足,URLCache会自动跳过缓存存储。
内容的提问来源于stack exchange,提问作者Omarj
相关产品推荐
相关产品推荐

