Alamofire(三) 实战分析

前言

前面分析了一波Request的整个流程和源码,本文是讲解到底在项目中如何实际使用,来满足对于网络请求的各种需求。

Adapter动态适配

 SessionManager.default.adapter = DLAdapter()
        SessionManager.default.request(myGetUrlString, method: .get, parameters: ["array":getJsonFromArray(array)])
            .response { (response) in
                debugPrint(response)
        }

class DLAdapter: RequestAdapter{
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        var request = urlRequest
        request.setValue("token_value", forHTTPHeaderField: "token");
        return request;
    }
}
  • 实现RequestAdapter,实现adapt方法,在这里面做了设置请求头token的操作
  • SessionManager.default.adapter = DLAdapter(),配置了Adapter后,所有的请求,都会带上个这个请求头的值
  • 还可以实现重定向功能等等,具体想统一对request做什么处理,要看项目的具体需求了
源码探索

探索里面的源码实现,我这里是把几处的源码都综合在一处了

open var adapter: RequestAdapter?
   
let task = try originalTask.task(session: session, adapter: adapter, queue: queue)

let urlRequest = try self.urlRequest.adapt(using: adapter)

func adapt(using adapter: RequestAdapter?) throws -> URLRequest {
        guard let adapter = adapter else { return self }
        return try adapter.adapt(self)
}

public protocol RequestAdapter {
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest
}

在创建task的时候会传递当前的adapter,这个adapter在这里try adapter.adapt(self)会执行,而且这个adapter找不到具体的实现,它是外界直接传递进来的,外界传递了则使用,外界不传递则不使用。

Retrier请求重试

使用方法很简单,和上面基本一样,实现RequestRetrier协议里的should方法即可

SessionManager.default.retrier = DLAdapter()
class DLAdapter: RequestAdapter,RequestRetrier{
    func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
        var request = urlRequest
        request.setValue("token_value", forHTTPHeaderField: "token");
        return request;
    }
    
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
        completion(true,1)
    }
}

  • 可以在should方法里根据条件判断是返回true还是false,true则表示需要重试
  • 在使用的时候只要设置SessionManager.default.retrier即可
  • 实用的地方很多,比如某个接口需要轮询重复查询,这种情况用上retrier很方便
源码探索

找到使用到retrier的方法

  open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
  /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
/// should be retried. Otherwise, complete the task by notifying the task delegate.
  if let retrier = retrier, let error = error {
            retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
                guard shouldRetry else { completeTask(session, task, error) ; return }

                DispatchQueue.utility.after(timeDelay) { [weak self] in
                    guard let strongSelf = self else { return }

                    let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false

                    if retrySucceeded, let task = request.task {
                        strongSelf[task] = request
                        return
                    } else {
                        completeTask(session, task, error)
                    }
                }
            }
        } else {
            completeTask(session, task, error)
        }
}
  • 看注释的说明是,如果错误发生并且设置了retrier,会同步询问request是否需要retrier,没发生错误就通知taskDelegate请求完成。
  • 这里面的方法就相当于一个递归,如果retrySucceeded一直为ture,则会一直调用下去,所以我们要注意在should方法中要有返回为false的情况,不然会一直递归下去

自定义验证validate

在实际相关项目中,我们可能有一些响应码需要特殊处理,这时候自定义验证就很适合使用了,如果能配上上面的retrier更加好用,返回error后再retrier

SessionManager.default.request(myGetUrlString, method: .get, parameters: ["array":getJsonFromArray(array)])
            .response { (response) in
                debugPrint(response)
            }.validate { (request, response, data) -> Request.ValidationResult in
                guard let _ = data else{
                    return .failure(NSError.init(domain: "test", code: 10089, userInfo: nil))
                }
                let code = response.statusCode
                if code == 404 {
                    return .failure(NSError.init(domain: "test2", code: 100800, userInfo: nil))
                }
                return .success
        }    

这里是自定义了两个error,在data为空和statusCode为404的时候都会返回自定义的error信息

    public func validate(_ validation: @escaping Validation) -> Self {
        let validationExecution: () -> Void = { [unowned self] in
            if
                let response = self.response,
                self.delegate.error == nil,
                case let .failure(error) = validation(self.request, response, self.delegate.data)
            {
                self.delegate.error = error
            }
        }

        validations.append(validationExecution)

        return self
    }

这里是把验证的闭包加到了validations里,然后在请求完成后,会把validations里的闭包都执行一遍

    open func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
      // Run all validations on the request before checking if an error occurred
        request.validations.forEach { $0() }
    }

TimeLine

Alamofire提供了TimeLine时间线,为了我们开发的便捷,能够通过Timeline快速得到这个请求的时间数据,做一些可能需要做的优化操作

timeline: Timeline: { 
"Request Start Time": 588099247.070,
"Initial Response Time": 588099272.474, 
"Request Completed Time": 588099272.475, 
"Serialization Completed Time": 588099272.475, 
"Latency": 25.404 secs, 
"Request Duration": 25.405 secs, 
"Serialization Duration": 0.000 secs, 
"Total Duration": 25.405 secs 
 }    

请求开始时间

open func resume() {
    if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }
}

添加请求完成时间记录

init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
    self.session = session
       // 省略无关代码,方便阅读
    delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
}

初始化响应时间

func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
    if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
}

时间轴设置

var timeline: Timeline {
    let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent()
    let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
    let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime

    return Timeline(
        requestStartTime: requestStartTime,
        initialResponseTime: initialResponseTime,
        requestCompletedTime: requestCompletedTime,
        serializationCompletedTime: CFAbsoluteTimeGetCurrent()
    )
}

初始化记录时间以及计算时间

public init(
    requestStartTime: CFAbsoluteTime = 0.0,
    initialResponseTime: CFAbsoluteTime = 0.0,
    requestCompletedTime: CFAbsoluteTime = 0.0,
    serializationCompletedTime: CFAbsoluteTime = 0.0)
{
    self.requestStartTime = requestStartTime
    self.initialResponseTime = initialResponseTime
    self.requestCompletedTime = requestCompletedTime
    self.serializationCompletedTime = serializationCompletedTime

    self.latency = initialResponseTime - requestStartTime
    self.requestDuration = requestCompletedTime - requestStartTime
    self.serializationDuration = serializationCompletedTime - requestCompletedTime
    self.totalDuration = serializationCompletedTime - requestStartTime
}

Result

Alamofire请求完数据后会返回一个response,但是这个不是我们最终的结果,还需要进行一些处理,才能返回成我们需要的。

public func response(
    queue: DispatchQueue? = nil,
    responseSerializer: T,
    completionHandler: @escaping (DataResponse) -> Void)
    -> Self
{
    delegate.queue.addOperation {
        let result = responseSerializer.serializeResponse(
            self.request,
            self.response,
            self.delegate.data,
            self.delegate.error
        )

        var dataResponse = DataResponse(
            request: self.request,
            response: self.response,
            data: self.delegate.data,
            result: result,
            timeline: self.timeline
        )
    }
    return self
}

可以看到这个result是在经过responseSerializer.serializeResponse序列化后返回的,DataResponse

public enum Result {
    case success(Value)
    case failure(Error)
    
   // 提供成功还有失败的校验
    public var isSuccess: Bool {... }
    public var isFailure: Bool {...}
    public var value: Value? {...}
    public var error: Error? {... }
}

Result的枚举值只有success和failure,使用起来非常方便

总结

以上都是比较实用的Alamofire开发技巧,希望大家能熟练使用!

你可能感兴趣的:(Alamofire(三) 实战分析)