专栏名称: 知识小集
目录
相关文章推荐
航空工业  ·  长空雄鹰硬核战力护山河 ·  6 小时前  
航空工业  ·  外场日志:躬身平凡 成就不凡 ·  昨天  
兵团零距离  ·  我国拟修法促进民航业高质量发展 ·  昨天  
航空工业  ·  中航工业党组领导到相关单位开展调研工作 ·  2 天前  
51好读  ›  专栏  ›  知识小集

从数据流角度管窥 Moya 的实现(二):处理响应

知识小集  · 掘金  ·  · 2018-04-05 06:09

正文

从数据流角度管窥 Moya 的实现(二):处理响应

上一篇讲了 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 ,回传的只是裸数据,这样做有以下好处:

  1. Moya 只需要关注网络请求,能保持轻量;
  2. 不与具体的数据转换库耦合,方便扩展,让用户决定怎么去转换数据;同时减少依赖库;
  3. 回传裸数据,让用户去定义接口的数据格式,方便扩展;

不过 Moya 也为我们提供了几个转换方法,如下:

  • mapImage() 尝试把响应数据转化为 UIImage 实例 如果不成功将产生一个错误。
  • mapJSON() 尝试把响应数据映射成一个 JSON 对象,如果不成功将产生一个错误。
  • mapString() 把响应数据转化成一个字符串,如果不成功将产生一个错误。
  • mapString(atKeyPath:) 尝试把响应数据的 key Path 映射成一个字符串,如果不成功将产生一个错误。

在业务层可能能用得上这些方法,比如先将数据转换成 JSON ,再丢给其它库使用。







请到「今天看啥」查看全文