通过 Moya+RxSwift+Argo 完成网络请求
来源:互联网 发布:中国地质大学北京知乎 编辑:程序博客网 时间:2024/06/04 18:34
https://blog.callmewhy.com/2015/11/01/moya-rxswift-argo-lets-go/
最近在新项目中尝试使用 Moya+RxSwift+Argo 进行网络请求和解析,感觉还阔以,再来给大家安利一波。
Moya
Moya
是一个基于 Alamofire
的更高层网络请求封装,深入学习请参见官方文档:Moya/Docs。
使用 Moya
之后网络请求一般长了这样:
provider.request(.UserProfile("ashfurrow")) { (data, statusCode, response, error) inif let data = data {// do something with the data}}
Moya
提供了很多不错的特性,其中我感觉最棒的是 stub
,配合 sampleData
分分钟就完成了单元测试:
private let provider = MoyaProvider<ItemAPI>(stubClosure: MoyaProvider.ImmediatelyStub)
注意这里的 MoyaProvider.ImmediatelyStub
,我原以为它是个枚举类型,看了 MoyaProvider
定义发现这里应该传个 closure
,看了 ImmediatelyStub
的定义发现原来它是个类方法:
public typealias StubClosure = Target -> Moya.StubBehavioroverride public init(stubClosure: StubClosure = MoyaProvider.NeverStub, ...) {}public final class func ImmediatelyStub(_: Target) -> Moya.StubBehavior {return .Immediate}
如果想打印每次请求的参数,在组装 endpoint
的时候打印即可:
private func endpointMapping<Target: MoyaTarget>(target: Target) -> Endpoint<Target> {if let parameters = target.parameters {log.verbose("\(parameters)")}return MoyaProvider.DefaultEndpointMapping(target)}private let provider = RxMoyaProvider<ItemAPI>(endpointClosure: endpointMapping)
RxSwift
RxSwift
前面强行安利过两波,在此不再赘述啦,Moya
本身提供了 RxSwift
扩展,可以无缝衔接 RxSwift
和 ReactiveCocoa
,于是打开方式变成了这样:
private let provider = RxMoyaProvider<ItemAPI>()private var disposeBag = DisposeBag()extension ItemAPI {static func getNewItems(completion: [Item] -> Void) {disposeBag = DisposeBag()provider.request(.GetItems()).subscribe(onNext: { items incompletion(items)}).addDisposableTo(disposeBag)}}
Moya
的核心开发者、同时也是 Artsy 的成员:Ash Furrow, 在 AltConf 做过一次 《Functional Reactive Awesomeness With Swift》 的分享,推荐大家看一下,很可爱的!
Argo
Argo
是 thoughtbot
开源的函数式 JSON
解析转换库。说到 thoughtbot
就不得不提他司关于 JSON
解析质量很高的一系列文章:
- Efficient JSON in Swift with Functional Concepts and Generics
- Real World JSON Parsing with Swift
- Parsing Embedded JSON and Arrays in Swift
- Functional Swift for Dealing with Optional Values
Argo
基本上就是沿着这些文章的思路写出来的,相关的库还有 Runes 和 Curry。
使用 Argo
做 JSON
解析很有意思,大致长这样:
struct Item {let id: Stringlet url: String}extension Item: Decodable {static func decode(j: JSON) -> Decoded<Item> {return curry(Item.init)<^> j <| "id"<*> j <| "url"}}
至于这其中各种符号的缘由,在几篇博客中都有讲解,还是挺有意思滴。
All
说完这三者,如何把它们串起来呢?Emergence 中的 Observable/Networking 给了我们答案。稍微整理后如下:
enum ORMError : ErrorType {case ORMNoRepresentorcase ORMNotSuccessfulHTTPcase ORMNoDatacase ORMCouldNotMakeObjectError}extension Observable {private func resultFromJSON<T: Decodable>(object:[String: AnyObject], classType: T.Type) -> T? {let decoded = classType.decode(JSON.parse(object))switch decoded {case .Success(let result):return result as? Tcase .Failure(let error):log.error("\(error)")return nil}}func mapSuccessfulHTTPToObject<T: Decodable>(type: T.Type) -> Observable<T> {return map { representor inguard let response = representor as? MoyaResponse else {throw ORMError.ORMNoRepresentor}guard ((200...209) ~= response.statusCode) else {if let json = try? NSJSONSerialization.JSONObjectWithData(response.data, options: .AllowFragments) as? [String: AnyObject] {log.error("Got error message: \(json)")}throw ORMError.ORMNotSuccessfulHTTP}do {guard let json = try NSJSONSerialization.JSONObjectWithData(response.data, options: .AllowFragments) as? [String: AnyObject] else {throw ORMError.ORMCouldNotMakeObjectError}return self.resultFromJSON(json, classType:type)!} catch {throw ORMError.ORMCouldNotMakeObjectError}}}func mapSuccessfulHTTPToObjectArray<T: Decodable>(type: T.Type) -> Observable<[T]> {return map { response inguard let response = response as? MoyaResponse else {throw ORMError.ORMNoRepresentor}// Allow successful HTTP codesguard ((200...209) ~= response.statusCode) else {if let json = try? NSJSONSerialization.JSONObjectWithData(response.data, options: .AllowFragments) as? [String: AnyObject] {log.error("Got error message: \(json)")}throw ORMError.ORMNotSuccessfulHTTP}do {guard let json = try NSJSONSerialization.JSONObjectWithData(response.data, options: .AllowFragments) as? [[String : AnyObject]] else {throw ORMError.ORMCouldNotMakeObjectError}// Objects are not guaranteed, thus cannot directly map.var objects = [T]()for dict in json {if let obj = self.resultFromJSON(dict, classType:type) {objects.append(obj)}}return objects} catch {throw ORMError.ORMCouldNotMakeObjectError}}}}
这样在调用的时候就很舒服了,以前面的 Item
为例:
private let provider = RxMoyaProvider<ItemAPI>()private var disposeBag = DisposeBag()extension ItemAPI {static func getNewItems(records:[Record] = [], needCount: Int, completion: [Item] -> Void) {disposeBag = DisposeBag()provider.request(.AddRecords(records, needCount)).mapSuccessfulHTTPToObjectArray(Item).subscribe(onNext: { items incompletion(items)}).addDisposableTo(disposeBag)}}
一个 mapSuccessfulHTTPToObjectArray
方法,直接将 JSON
字符串转换成了 Item
对象,并且传入了后面的数据流中,所以在 onNext
订阅的时候传入的就是 [Item]
数据,并且这个转换过程还是可以复用的,且适用于所有网络请求中 JSON
和 Model
的转换。爽就一个字,我只说一次。
爽!
Next
匆匆读了一点 Emergence 和 Eidolon 的项目源码,没有深入不过已经受益匪浅。通过 bundle 管理 id 和 key 直接解决了我当初纠结已久的『完整项目开源如何优雅地保留 git 记录且保护项目隐私』的问题,还有 Moya/RxSwift
和 Moya/ReactiveCocoa
这种子模块化处理也在共有模块管理这个问题上给了我一些启发。
真是很喜欢 Artsy 这样的团队,大家都一起做着自己喜欢的事情,还能站着把钱赚了。
所幸的是我也可以这样做自己喜欢的事情了,不过不赚钱。具体状况后面单独开一篇闲扯扯。
碎告。
参考资料:
- RxSwift
- Moya
- Argo
- Emergence
- Eidolon
- Efficient JSON in Swift with Functional Concepts and Generics
- Real World JSON Parsing with Swift
- Parsing Embedded JSON and Arrays in Swift
- Functional Swift for Dealing with Optional Values
- 通过 Moya+RxSwift+Argo 完成网络请求
- moya + RxSwift 进行网络请求
- 用 RxSwift + Moya 写出优雅的网络请求代码
- moya+RxSwift+HandyJSON 学习
- Swift网络请求(Moya篇)
- RxSwift+Moya之项目实战
- iOS 【如何写出最简洁优雅的网络封装 Moya + RxSwift】
- 基于[Moya]-打造更现代化的网络请求库
- 系统性学习Moya+Alamofire+RxSwift+ObjectMapper的配合使用
- RxJava + Retrofit完成网络请求
- RxJava + Retrofit完成网络请求
- Retrofit+Rxjava完成网络请求
- 关于RxSwift MVVM flatMapLatest 点击事件网络请求失败整个序列结束
- 通过HttpURLConnection进行网络请求
- RxSwift
- RxSwift
- RxSwift
- RxSwift
- Android NuPlayer播放框架
- 链表-python-leetcode 83 Remove Duplicates from Sorted List
- 对象池与池管理
- qcom android GPS 启动流程
- vue-cli项目导入jquery插件(其他插件类似)
- 通过 Moya+RxSwift+Argo 完成网络请求
- poj2186 Popular Cows(强连通分量)
- 51nod 1297 管理二叉树
- 游戏编程入门(6):开发 Brainiac(记忆对对碰) 游戏
- Maven 进阶---自定义工具类,在本地仓库存储并使用 jar
- 获取本地视频,网络视频的第一帧图片,使之显示出来
- Redis技巧
- iOS 类的抽象和继承:类族与工厂模式~详解
- Ubuntu 下 安装 Phantomjs