上一篇讲了
Moya
构建和发起请求的数据流,从
Target -> Endpoint -> Request
这一套路清晰明了。现在我们来讲讲
Moya
数据返回的流程。再一次祭出那张图(图片来自参考2)。
好了,看完这张图就可以关了,下面基本上可以不用看了。如有闲情,那就再听我唠叨一下。
在这里,我们依然跟上一篇一样,避开各种错误分支流程。
接收数据及回传
在
Moya
层发出数据请求后,剩下的工作就是
Alamofire
去处理了。至于
Alamofire
如何发起请求以及接收响应,有兴趣可以去研究下代码或者看这方面的代码分析,在这不多讲了。我们只考虑
Moya
这一层的处理。
我们在发起请求的位置可以找到接收响应数据的代码:
progressAlamoRequest = progressAlamoRequest.response(callbackQueue: callbackQueue, completionHandler: completionHandler)
progressAlamoRequest.resume()
应该比较熟悉了,在这里可以看到接收响应的处理器
completionHandler
。在这个处理器里,首先会把一个响应相关的信息丢到
convertResponseToResult()
方法里面做个包装,把这些信息封装在一个
Response
对象里面,再打包到
Result
中。我们来看看
Response
对象:
public final class Response: CustomDebugStringConvertible, Equatable {
public let statusCode: Int // 状态码
public let data: Data // 数据
public let request: URLRequest? // 对应的请求对象
public let response: HTTPURLResponse? // 响应对象
......
}
Response
类本体没有太多信息,不过它的扩展提供了不少有用的方法,包括根据响应状态码过滤请求,以及我们很关心的数据转换。数据转换一会我们单独讲,先把主流程梳理一下。
从
convertResponseToResult()
返回的
Result
会被传入
completion()
回调中,
let completionHandler: RequestableCompletion = { response, request, data, error in
let result = convertResponseToResult(response, request: request, data: data, error: error)
// Inform all plugins about the response
......
completion(result)
}
这个
completion
是从
requestNormal()
中传进来的,我们来看看。
let networkCompletion: Moya.Completion = { result in
if self.trackInflights {
self.inflightRequests[endpoint]?.forEach { $0(result) }
objc_sync_enter(self)
self.inflightRequests.removeValue(forKey: endpoint)
objc_sync_exit(self)
} else {
pluginsWithCompletion(result)
}
}
我们只关注
pluginsWithCompletion()
:
let pluginsWithCompletion: Moya.Completion = { result in
let processedResult = self.plugins.reduce(result) { $1.process($0, target: target) }
completion(processedResult)
}
这里通过插件对
result
进行处理后,最后调用
completion()
,这个
completion
就是由业务层代码传进来的回调了。嗯,终于回了口气。来看看调用:
gitHubProvider.request(.userRepositories(username)) { result in
......
}
这样就回到我们的业务代码了。至此整个数据流又回到了业务层。
转换数据
业务层接收到数据后,就可以直接使用数据。不过这里我们获取到的是一个
Response
对象,也就是说我们获取到的基本上是一个没有经过多少处理的裸数据。对于业务层开发来讲,将这些数据转换为直接可以使用的对象或结构体是一个强需求。这也正是各种数据映射库的用武之地。
有些网络层的封装,可能会将这个映射操作直接耦合在网络封装层,这样返回给业务层的就是一个可以直接使用的数据对象或者数据对象数组。不过
Moya
没有这么做,它甚至没有把
Moya
转换为
JSON
,回传的只是裸数据,这样做有以下好处:
- Moya 只需要关注网络请求,能保持轻量;
- 不与具体的数据转换库耦合,方便扩展,让用户决定怎么去转换数据;同时减少依赖库;
- 回传裸数据,让用户去定义接口的数据格式,方便扩展;
不过
Moya
也为我们提供了几个转换方法,如下:
-
mapImage()
尝试把响应数据转化为UIImage
实例 如果不成功将产生一个错误。 -
mapJSON()
尝试把响应数据映射成一个JSON
对象,如果不成功将产生一个错误。 -
mapString()
把响应数据转化成一个字符串,如果不成功将产生一个错误。 -
mapString(atKeyPath:)
尝试把响应数据的key Path
映射成一个字符串,如果不成功将产生一个错误。
在业务层可能能用得上这些方法,比如先将数据转换成
JSON
,再丢给其它库使用。