从数据流角度管窥 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 inlet result = convertResponseToResult(response, request: request, data: data, error: error)// Inform all plugins about the response......completion(result)
}
复制代码
这个 completion
是从 requestNormal()
中传进来的,我们来看看。
let networkCompletion: Moya.Completion = { result inif 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 inlet 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
,再丢给其它库使用。
在 Github
上,Moya
提供了一些 JSON
序列化的库,可以参考一下。不过 Swift 4
之后的 Codable
也许能统一江湖。
测试插桩
Moya
还有一个很好的特性,就是为本地 mock
数据提供了一个很好的支持。
要想使用本地 mock
功能,可以在创建 MoyaProvider
时传入 stubClosure
参数,值为 MoyaProvider.immediatelyStub
或者 MoyaProvider.delayedStub
,其值被赋值给 MoyaProvider
的 stubClosure
属性:
public typealias StubClosure = (Target) -> Moya.StubBehavior/// A closure responsible for determining the stubbing behavior
/// of a request for a given `TargetType`.
open let stubClosure: StubClosure
复制代码
实际上是为了最终获取 StubBehavior
。这个值对于 Target
到 Request
的构建过程没有影响,只是影响到发起请求的操作。我们在此不再详细描述,通过流程图来看看:
- 在
performRequest()
中根据stubBehavior
来判断,而进入stubRequest()
方法; - 在
stubRequest()
方法中,使用createStubFunction
创建stub()
闭包,并根据stubBehavior
来决定stub()
的执行方式和时机; - 最主要的操作是在
stub()
中,根据endpoint.sampleResponseClosure()
来处理sample
数据;
后面的流程就是数据返回了。
总结
至此,基于数据流,我们大概浏览了一下 Moya
的实现。当然还有一些功能没有涉及到,如进度处理、请求跟踪等,有兴趣可以看下源码。
个人觉得 Moya
最有意思的就是通过 Target
和 enum
来描述一组接口,同时可以通过 MultiTarget
来将接口分组,给了我们很大的空间。不过有利有弊,enum
带来便利的同时,也可能会带来大量的 switch...case
代码,我们需要根据实际情况来组织代码。
参考
- 官方文档
https://github.com/Moya/Moya/blob/master/docs/README.md
- Moya的设计之道
https://github.com/LeoMobileDeveloper/Blogs/blob/master/Swift/AnaylizeMoya.md
扫描关注 知识小集
从数据流角度管窥 Moya 的实现(二):处理响应相关推荐
- 【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )
Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...
- spring boot / cloud (二) 规范响应格式以及统一异常处理
spring boot / cloud (二) 规范响应格式以及统一异常处理 前言 为什么规范响应格式? 我认为,采用预先约定好的数据格式,将返回数据(无论是正常的还是异常的)规范起来,有助于提高团队 ...
- 数据流角度看DSO(一)
此系列博客是在这篇博客( DSO代码梳理(一) )的基础继续往下分析的,我觉得从数据流的角度可能能够更加理解算法原理.DSO代码梳理(一) 这篇博客主要介绍了DSO运行时候需要指定的参数. 运行前的准 ...
- 从C语言的角度重构数据结构系列(二)-如何衡量一个算法的优劣?
前言 在学习具体的数据结构和算法之前,每一位初学者都要掌握一个技能,即善于运用时间复杂度和空间复杂度来衡量一个算法的运行效率. 在这里给自己打个广告,需要的小伙伴请自行订阅. python快速学习实战 ...
- 源码角度解释fragment的坑(二)
如果你已经使用过fragment,我先提出几个问题. 1.按返回键的时候,如果当前的fragment是加入到栈的(其实这个说法不是很准确,因为真正加入栈的是操作fragment的事务),那么这个fra ...
- C语言中如何衡量算法的正确性,从C语言的角度重构数据结构系列(二)-如何衡量一个算法的优劣?...
前言 在学习具体的数据结构和算法之前,每一位初学者都要掌握一个技能,即善于运用时间复杂度和空间复杂度来衡量一个算法的运行效率. 在这里给自己打个广告,需要的小伙伴请自行订阅. python快速学习实战 ...
- 从技术实现角度看信贷ABS业务(二)
目录 1.ABS的过程 2.ABS的参与对象 3.信贷ABS的收益切割模式 1.ABS的过程 ABS的一般过程可以参考下图,详细内容可以参考<中金债市宝典>(https://mp.weix ...
- matlabeig函数根据什么原理_vue3.0 源码解析二 :响应式原理(下)
一 回顾上文 上节我们讲了数据绑定proxy原理,vue3.0用到的基本的拦截器,以及reactive入口等等.调用reactive建立响应式,首先通过判断数据类型来确定使用的hander,然后创建p ...
- CSS - 响应式布局(二)响应式栅格系统
目录 响应式栅格系统 栅格 栅格系统 响应式栅格系统 BootStrap响应式栅格系统 简单说明 利用SCSS实现BootStrap的响应式栅格系统 响应式栅格系统 栅格 在弄清楚响应式栅格系统前,我 ...
- 《CLR Via C# 第3版》笔记之(二) - 响应文件
主要内容: 默认的响应文件 自定义响应文件 1. 默认的响应文件 .net在编译的时候会引用很多其他的程序集,最基本的比如System.dll,System.core.dll等等. 我们通过命令行编译 ...
最新文章
- Warshall算法多源点之间最短路径的算法最短距离
- linux(以ubuntu为例)下Android利用ant自动编译、修改配置文件、批量多渠道,打包生成apk文件...
- asp 随机读取ID之Access
- 普渡大学李攀:好的图表示到底是什么?
- 2.MOC文件解读(上)——MOC文件中的数据
- Linux rpm 命令参数
- 单交换机VLAN虚拟局域网划分
- SAP CRM呼叫中心的邮件发送实现 - Function module CRM_EMAIL_SEND_EMAIL
- 将Auth0 OIDC(OAUTH 2)与授权(组和角色)集成
- 蚂蚁金服分布式事务开源以及实践 | SOFA 开源一周年献礼
- Mysql数据库,项目需求需要数据还原的数据表结构构思方案
- 创建设计模式 - 抽象工厂设计模式
- 【智慧楼宇项目】nodemcu(lua)控制HLW8032电计量模块
- Pigeon 工具类ExtensionLoader
- 全部重点排污企业名录(整理成Excel更新至2021年)
- Kafka的灵魂伴侣Logi-KafkaManger(3)之运维管控--集群列表
- Linux的判断两个字符串是否相等
- PMP 项目管理 考前专题(02)敏捷开发专题总结
- rockbox主题包安装_WaveCN.com - 站长手记 - 站长手记 - Rockbox Utility介绍及Rockbox快速安装简介...
- redis命令详解与使用场景举例——Server(服务器)