前言
公司之前的项目是由其他同事搭建的,随着公司业务的拓展,网络请求随之增加。网络工具类内部的代码愈发庞大,最终难以管理。为此寻找一个可行的解决方案,顺便学习一下RxSwift的使用。不说那么多底层原理,直接咱就说怎么用、怎么写,通俗易懂。文章附demo源码,由于本人也在学习中,所以代码中难免存在疏漏,存在问题可以互相讨论。
环境配置
- Xcode 8.3
- Swift 3
cocoapods
- Alamofire
- ObjectMapper
- Moya
- RxSwift
- RxCocoa
- PromiseKit
其中,数据解析部分(ObjectMapper)我们根据各自公司的习惯,可以选择Argo,SwiftyJSON等等其他框架,这不影响本博客内容。
实例
Moya部分
Moya是一个网络抽象层库。它在Alamofire基础上提供了一系列简单的抽象接口,让客户端代码不用去直接调用Alamofire,同时提供了很多实用的功能。
创建请求
Moya的TargetType协议规定使用枚举来创建网络请求,我们可以通过枚举来区分不同业务的网络请求。例如:
1 | enum UserAPI { |
这里我们使用demo里ApiExample的实例,分别表示一个get、post和一个上传图片的请求,下载的请求一般只有特殊的业务里才会用到,这里不做展示。
1 | enum ApiExample { |
定义请求枚举完成后,我们需要在枚举的拓展里实现Moya的TargetType协议。
1 | extension ApiExample: TargetType { |
这里对几个特殊的参数做出解释。sampleData是单元测试等等需要的假数据,只有在有测试的需求下才用得到,一般我们返回一个空的数据就可以。parameterEncoding是参数编码方式,通常我们使用URLEncoding就可以,根据不同的需求去选择,如实例中注释代码。
插件机制
如果我们想在请求前和请求后做一些操作,Moya提供自定义请求插件来实现这一需求。比如我们要做请求完成前显示菊花,请求完成或失败后隐藏菊花,我们可以这样做。自定义插件需要遵守PluginType协议,根据不同需求实现协议里的方法。实例:
1 | public final class RequestLoadingPlugin: PluginType { |
ObjectMapper解析数据
我们使用ObjectMapper解析数据映射为model,ObjectMapper的使用这里不作介绍,请看官方文档。
我们为RxSwift中Observable写一个拓展,用于解析数据。通常我们服务器后台返回的数据格式类似以下形式:
1 | // success |
所以在拓展里面提供两个方法去解析模型和数组,并可选的传入key值来解析返回的json数据中字典对应的key值。
1 | func mapObject<T: Mappable>(type: T.Type, key: String? = nil) -> Observable<T> { |
另外我们可以自定义错误类型来处理数据解析中发生的错误。
1 | enum RxSwiftMoyaError: String { |
有时候请求是成功的,但是请求内容是错误的,错误信息由我们的服务器返回,如前文提及。所以我们在这个拓展里提供了一个方法解析服务器返回的错误信息,若有错误则抛出,无则返回nil
1 | fileprivate func parseError(response: [String: Any]?) -> NSError? { |
异步编程 PromiseKit
现代编程语言都很好的支持了异步编程,因此在swift编程中,拥有功能强大且轻量级的异步编程工具的需求变得很强烈。
这是PromiseKit中提到的,所以我们不甘落后,也想也来一发。
如官方文档中提到的异步链式调用,这很常见。详细使用请看官方文档,这里不做介绍。
1 | login().then { json in |
例如,在本demo中我们使用MVVM模式。在viewModel中我们处理网络请求。在demo中ViewModelExample文件内,我们书写以下代码,来创建一个Promise(异步任务)。
1 | func getHomepagePageData() -> Promise<HomepageData> { |
我们使用Moya的RxSwift拓展,来创建一个Provider,用于请求数据。在这里,我们创建Provider时可以自主选择插件,用于不同的需求。例如有些网络请求我们并不希望用户看到,所以不需要出现加载提示(如菊花,等等),我们在创建时就不需要加入插件。反之,我们可以在创建Provider时加入所需的插件以适应不同的需求。因此Moya变得更加灵活。实例:
1 | let provider = RxMoyaProvider<ApiExample>(plugins: [RequestLoadingPlugin(),NetworkLogger()]) |
使用实例
我们在控制器里添加一个viewModel的实例,然后在网络请求的方法是实现viewModel中的方法即可。实例:
1 | lazy var viewModel = ViewModelExample() |
通过PromiseKit直接链式调用,我们很简洁的处理数据和错误。通过Moya和RxSwift,我们发送请求和解析数据也变得灵活机动干净利落,不拖泥带水。同时因为我们通过枚举来管理不同业务的请求接口,代码逻辑也变得清晰。
总之,Moya+PromiseKit+Swift 所谓优雅的书写网络请求,非常值得尝试一下!