前言
前面分析了一波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<T: DataResponseSerializerProtocol>(
queue: DispatchQueue? = nil,
responseSerializer: T,
completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void)
-> Self
{
delegate.queue.addOperation {
let result = responseSerializer.serializeResponse(
self.request,
self.response,
self.delegate.data,
self.delegate.error
)
var dataResponse = DataResponse<T.SerializedObject>(
request: self.request,
response: self.response,
data: self.delegate.data,
result: result,
timeline: self.timeline
)
}
return self
}
可以看到这个result
是在经过responseSerializer.serializeResponse
序列化后返回的,DataResponse
public enum Result<Value> {
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开发技巧,希望大家能熟练使用!