原标题:通过Moya+RxSwift+Argo完成网络请求
作者:@请叫我汪二 授权本站转载。
最近在新项目中尝试使用 Moya+RxSwift+Argo 进行网络请求和解析,感觉还阔以,再来给大家安利一波。
MoyaMoya
是一个基于 Alamofire
的更高层网络请求封装,深入学习请参见官方文档:Moya/Docs。
使用 Moya
之后网络请求一般长了这样:
provider.request(.UserProfile("ashfurrow")){(data,statusCode,response,error)in ifletdata=data{ //dosomethingwiththedata } }
Moya
提供了很多不错的特性,其中我感觉最棒的是 stub
,配合 sampleData
分分钟就完成了单元测试:
privateletprovider=MoyaProvider(stubClosure:MoyaProvider.ImmediatelyStub)
注意这里的 MoyaProvider.ImmediatelyStub
,我原以为它是个枚举类型,看了 MoyaProvider
定义发现这里应该传个 closure
,看了 ImmediatelyStub
的定义发现原来它是个类方法:
publictypealiasStubClosure=Target->Moya.StubBehavior overridepublicinit(stubClosure:StubClosure=MoyaProvider.NeverStub,...){ } publicfinalclassfuncImmediatelyStub(_:Target)->Moya.StubBehavior{ return.Immediate }
如果想打印每次请求的参数,在组装 endpoint
的时候打印即可:
privatefuncendpointMapping(target:Target)->Endpoint{ ifletparameters=target.parameters{ log.verbose("\(parameters)") } returnMoyaProvider.DefaultEndpointMapping(target) } privateletprovider=RxMoyaProvider(endpointClosure:endpointMapping)RxSwift
RxSwift
前面强行安利过两波,在此不再赘述啦,Moya
本身提供了 RxSwift
扩展,可以无缝衔接 RxSwift
和 ReactiveCocoa
,于是打开方式变成了这样:
privateletprovider=RxMoyaProvider() privatevardisposeBag=DisposeBag() extensionItemAPI{ staticfuncgetNewItems(completion:[Item]->Void){ disposeBag=DisposeBag() provider .request(.GetItems()) .subscribe( onNext:{itemsin completion(items) } ) .addDisposableTo(disposeBag) } }
Moya
的核心开发者、同时也是 Artsy 的成员:Ash Furrow, 在 AltConf 做过一次 《Functional Reactive Awesomeness With Swift》 的分享,推荐大家看一下,很可爱的!
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
解析很有意思,大致长这样:
structItem{ letid:String leturl:String } extensionItem:Decodable{ staticfuncdecode(j:JSON)->Decoded{ returncurry(Item.init) j<|"id" j<|"url" } }
至于这其中各种符号的缘由,在几篇博客中都有讲解,还是挺有意思滴。
All说完这三者,如何把它们串起来呢?Emergence 中的 Observable/Networking 给了我们答案。稍微整理后如下:
enumORMError:ErrorType{ caseORMNoRepresentor caseORMNotSuccessfulHTTP caseORMNoData caseORMCouldNotMakeObjectError } extensionObservable{ privatefuncresultFromJSON(object:[String:AnyObject],classType:T.Type)->T?{ letdecoded=classType.decode(JSON.parse(object)) switchdecoded{ case.Success(letresult): returnresultas?T case.Failure(leterror): log.error("\(error)") returnnil } } funcmapSuccessfulHTTPToObject(type:T.Type)->Observable{ returnmap{representorin guardletresponse=representoras?MoyaResponseelse{ throwORMError.ORMNoRepresentor } guard((200...209)~=response.statusCode)else{ ifletjson=try?NSJSONSerialization.JSONObjectWithData(response.data,options:.AllowFragments)as?[String:AnyObject]{ log.error("Goterrormessage:\(json)") } throwORMError.ORMNotSuccessfulHTTP } do{ guardletjson=tryNSJSONSerialization.JSONObjectWithData(response.data,options:.AllowFragments)as?[String:AnyObject]else{ throwORMError.ORMCouldNotMakeObjectError } returnself.resultFromJSON(json,classType:type)! }catch{ throwORMError.ORMCouldNotMakeObjectError } } } funcmapSuccessfulHTTPToObjectArray(type:T.Type)->Observable{ returnmap{responsein guardletresponse=responseas?MoyaResponseelse{ throwORMError.ORMNoRepresentor } //AllowsuccessfulHTTPcodes guard((200...209)~=response.statusCode)else{ ifletjson=try?NSJSONSerialization.JSONObjectWithData(response.data,options:.AllowFragments)as?[String:AnyObject]{ log.error("Goterrormessage:\(json)") } throwORMError.ORMNotSuccessfulHTTP } do{ guardletjson=tryNSJSONSerialization.JSONObjectWithData(response.data,options:.AllowFragments)as?[[String:AnyObject]]else{ throwORMError.ORMCouldNotMakeObjectError } //Objectsarenotguaranteed,thuscannotdirectlymap. varobjects=[T]() fordictinjson{ ifletobj=self.resultFromJSON(dict,classType:type){ objects.append(obj) } } returnobjects }catch{ throwORMError.ORMCouldNotMakeObjectError } } } }
这样在调用的时候就很舒服了,以前面的 Item
为例:
privateletprovider=RxMoyaProvider() privatevardisposeBag=DisposeBag() extensionItemAPI{ staticfuncgetNewItems(records:[Record]=[],needCount:Int,completion:[Item]->Void){ disposeBag=DisposeBag() provider .request(.AddRecords(records,needCount)) .mapSuccessfulHTTPToObjectArray(Item) .subscribe( onNext:{itemsin completion(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
相关:
源码推荐(01.11B):iOS项目分层,Widget手机任务栏
iOS项目分层(上传者:踏浪帅)主项目中的分层主要包含四个模块,Main(主要)、Expand(扩展)、Resource(照片)、
苹果mac怎么共享屏幕?OS X 自带屏幕共享功能,支持拖拽以及文本拷贝,操作还相当简单,仅需要点几个按钮就
觅海宝怎么样?觅海宝好不好?觅海宝是什么呢?这是一个中欧海淘跨境电商平台,由英国贸易投资总署和英
RightFont怎么用?字体管理工具RightFont使用教程
RightFont是一款Mac平台的字体管理软件,很多设计师们都需要使用到它,今天PC6小编就为大家带来一篇详尽的R
1. 关于代码覆盖率衡量代码覆盖率有很多种层次,比如行覆盖率,函数/方法覆盖率,类覆盖率,分支覆盖率等等
本文由玩赚乐(www.banghui.org)– 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!
本文由玩赚乐(www.banghui.org)– 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!
应用的启动启动方式通常来说,在安卓中应用的启动方式分为两种:冷启动和热启动。1、冷启动:当启动应用时,
了解Java的垃圾回收(GC)原理能给我们带来什么好处?对于软件工程师来说,满足技术好奇心可算是一个,但重要
这是”成为GC专家系列”文章的第二篇。在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,