使用 Moya 处理缓存

Posted

技术标签:

【中文标题】使用 Moya 处理缓存【英文标题】:Cache Handling with Moya 【发布时间】:2018-01-30 08:16:40 【问题描述】:

我们在项目中将 Moya、RxSwift 和 Alamofire 实现为 pod。

有谁知道您如何使用这项技术控制每个 url 请求的缓存策略?

我在 Moya 的 GitHub 页面上阅读了很多问题,但仍然找不到任何东西。还尝试使用存储为 sampleData 文件的实际 json 响应,如下所示:

var sampleData: Data 
    guard let path = Bundle.main.path(forResource: "SampleAggregate", ofType: "txt") else 
        return "sampleData".utf8Encoded
    
    let sample = try? String(contentsOfFile: path, encoding: String.Encoding.utf8)
    return sample!.utf8Encoded


提前感谢任何专业提示:)

【问题讨论】:

【参考方案1】:

据我了解,解决此问题的“最干净”的方法是使用自定义 Moya 插件。这是一个实现:

protocol CachePolicyGettable 
    var cachePolicy: URLRequest.CachePolicy  get 
  

final class CachePolicyPlugin: PluginType 
    public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest 
        if let cachePolicyGettable = target as? CachePolicyGettable 
            var mutableRequest = request
            mutableRequest.cachePolicy = cachePolicyGettable.cachePolicy
            return mutableRequest
        

        return request
    

要真正使用这个插件,还需要两个步骤:

    插件应该像这样添加到您的 Moya 提供程序中:

    let moyaProvider = MoyaProvider<YourMoyaTarget>(plugins: [CachePolicyPlugin()])
    

    YourMoyaTarget应该符合CachePolicyGettable,从而为每个目标定义缓存策略:

    extension YourMoyaTarget: CachePolicyGettable 
        var cachePolicy: URLRequest.CachePolicy 
            switch self 
            case .sampleTarget, .someOtherSampleTarget:
                return .reloadIgnoringLocalCacheData
    
            default:
                return .useProtocolCachePolicy
            
        
    
    

注意:此方法使用协议将缓存策略与目标类型相关联;也可以通过传递给插件的闭包来实现这一点。然后,此类闭包将根据作为输入参数传递给闭包的目标类型来决定使用哪个缓存策略。

【讨论】:

非常好的答案。我只是想知道回复会保存多长时间。 实际的缓存是通过底层的 URLRequest 处理的——所以要了解它在做什么,你必须在那里做一些研究【参考方案2】:

根据@fredpi 的回答,我稍微改进了 Moya 的缓存插件。以下是我的版本:

import Foundation
import Moya

protocol CachePolicyGettable 
    var cachePolicy: URLRequest.CachePolicy  get 


final class NetworkDataCachingPlugin: PluginType 

    init (configuration: URLSessionConfiguration, inMemoryCapacity: Int, diskCapacity: Int, diskPath: String?) 
        configuration.urlCache = URLCache(memoryCapacity: inMemoryCapacity, diskCapacity: diskCapacity, diskPath: diskPath)
    

    func prepare(_ request: URLRequest, target: TargetType) -> URLRequest 
        if let cacheableTarget = target as? CachePolicyGettable 
            var mutableRequest = request
            mutableRequest.cachePolicy = cacheableTarget.cachePolicy
            return mutableRequest
        
        return request
    


extension NetworkApiService: CachePolicyGettable 
    var cachePolicy: URLRequest.CachePolicy 
        switch self 
        case .getUserProfile:
            return .returnCacheDataElseLoad
        default:
            return .useProtocolCachePolicy
        
    

为了清除缓存,您需要访问 urlRequest 对象/对象。如何检索 Moya 路由的 urlRequest,您可以在 following 主题中找到。

要清除缓存,您可以使用以下代码:

public func clearCache(urlRequests: [URLRequest] = []) 
    let provider = ... // your Moya provider
    guard let urlCache = provider.manager.session.configuration.urlCache else  return 
    if urlRequests.isEmpty 
        urlCache.removeAllCachedResponses()
     else 
        urlRequests.forEach  urlCache.removeCachedResponse(for: $0) 
    

【讨论】:

【参考方案3】:

子类MoyaProvider 并组合requestClosure

它应该看起来像:

final class MyProvider<Target: TargetType>: MoyaProvider<Target> 

    public init(
        endpointClosure: @escaping EndpointClosure = MoyaProvider.defaultEndpointMapping,
        requestClosure: @escaping RequestClosure = MoyaProvider.defaultRequestMapping,
        stubClosure: @escaping StubClosure = MoyaProvider.neverStub,
        manager: Manager = MoyaProvider<Target>.defaultAlamofireManager(),
        plugins: [PluginType] = [],
        trackInflights: Bool = false
    ) 
        super.init(
            endpointClosure: endpointClosure,
            requestClosure:  endpoint, closure in
                var request = try! endpoint.urlRequest() //Feel free to embed proper error handling
                if request.url == URL(string: "http://google.com")! 
                    request.cachePolicy = .returnCacheDataDontLoad
                 else 
                    request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
                
                closure(.success(request))
            ,
            stubClosure: stubClosure,
            manager: manager,
            plugins: plugins,
            trackInflights: trackInflights
        )
    


【讨论】:

【参考方案4】:

如果您也想禁用存储的 cookie

request.httpShouldHandleCookies = false

【讨论】:

以上是关于使用 Moya 处理缓存的主要内容,如果未能解决你的问题,请参考以下文章

为啥在浏览器缓存处理缓存时使用服务工作者进行缓存?

Moya使用

处理 UITableView 绑定中的连接错误(Moya、RxSwift、RxCocoa)

缓存处理类(MemoryCache结合文件缓存)

Okhttp解析(五)缓存的处理

使用 Moya 获取响应标头