通过Moya+RxSwift+Argo完成网络请求
2016-01-16 19:20:29 | 来源:玩转帮会 | 投稿:佚名 | 编辑:小柯

原标题:通过Moya+RxSwift+Argo完成网络请求

作者:@请叫我汪二 授权本站转载。

最近在新项目中尝试使用 Moya+RxSwift+Argo 进行网络请求和解析,感觉还阔以,再来给大家安利一波。

Moya

Moya 是一个基于 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 扩展,可以无缝衔接 RxSwiftReactiveCocoa ,于是打开方式变成了这样:

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

Argothoughtbot 开源的函数式 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。

使用 ArgoJSON 解析很有意思,大致长这样:

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] 数据,并且这个转换过程还是可以复用的,且适用于所有网络请求中 JSONModel 的转换。爽就一个字,我只说一次。

爽!

Next

匆匆读了一点 Emergence 和 Eidolon 的项目源码,没有深入不过已经受益匪浅。通过 bundle 管理 id 和 key 直接解决了我当初纠结已久的『完整项目开源如何优雅地保留 git 记录且保护项目隐私』的问题,还有 Moya/RxSwiftMoya/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

tags:

上一篇  下一篇

相关:

源码推荐(01.11B):iOS项目分层,Widget手机任务栏

iOS项目分层(上传者:踏浪帅)主项目中的分层主要包含四个模块,Main(主要)、Expand(扩展)、Resource(照片)、

苹果mac怎么共享屏幕?mac屏幕共享设置教程

苹果mac怎么共享屏幕?OS X 自带屏幕共享功能,支持拖拽以及文本拷贝,操作还相当简单,仅需要点几个按钮就

觅海宝怎么样?觅海宝好不好?

  觅海宝怎么样?觅海宝好不好?觅海宝是什么呢?这是一个中欧海淘跨境电商平台,由英国贸易投资总署和英

RightFont怎么用?字体管理工具RightFont使用教程

RightFont是一款Mac平台的字体管理软件,很多设计师们都需要使用到它,今天PC6小编就为大家带来一篇详尽的R

Web端PHP代码函数覆盖率测试解决方案

1. 关于代码覆盖率衡量代码覆盖率有很多种层次,比如行覆盖率,函数/方法覆盖率,类覆盖率,分支覆盖率等等

我从1万小时编程中学到了什么

本文由玩赚乐(www.banghui.org)– 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

回顾2015年最大的7个App趋势

本文由玩赚乐(www.banghui.org)– 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

Android性能优化之加快应用启动速度

应用的启动启动方式通常来说,在安卓中应用的启动方式分为两种:冷启动和热启动。1、冷启动:当启动应用时,

JavaGC专家系列1:理解Java垃圾回收

了解Java的垃圾回收(GC)原理能给我们带来什么好处?对于软件工程师来说,满足技术好奇心可算是一个,但重要

JavaGC专家系列2:Java垃圾回收的监控

这是”成为GC专家系列”文章的第二篇。在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,

站长推荐: