我博客中前面的文章详细地介绍过了RxSwift和Moya,看过这些文章的小伙伴应该也知道RxSwift和Moya的使用。如果把他们两个结合起来使用就可以产生排山倒海,气吞山河的惊恐效果(有点夸张藍),不信?那你就来看看他们两个结合怎么写出一个你没有见过的网络请求。
我们就拿今日头条的首页分类数据请求作为例子,他的json格式如下(有精简):
{ "message": "success", "data": { "version": "34632081485|14|1483794649", "data": [ { "category": "news_hot", "name": "热点" }, { "category": "video", "name": "视频" }, ....... ] } }
正常的网络请求和使用Moya的网络请求请参考前一篇文章 Moya入坑记-用法解读篇 。
我们先来建立Model,这里我们使用一个库叫做 ObjectMapper 的库,具体用法大伙可以参考一下github的仓库:
classModel:Mappable{ var category: String? var name: String? required init?(map: Map) { } funcmapping(map: Map) { category <- map["category"] name <- map["name"] } }
然后就是建立我们使用Moya请求的Service(有部分省略,具体可以参考 demo )
let baseURL = "https://iu.snssdk.com" let articlePath = "/article/category/get_subscribed/v1/" let iid = 6253487170 let NewsProvider = RxMoyaProvider<NewsService>() //使用RxSwift扩展建立Provider public enumNewsService{ case category } extensionNewsService:TargetType{ public var baseURL: URL { return URL(string: "https://iu.snssdk.com")! } public var path: String { switch self { case .category: return articlePath } } public var method: Moya.Method { switch self { case .category: return .get } } public var parameters: [String : Any]? { switch self { case .category: return ["iid": iid] } } .......... }
然后在ViewModel中我们使用Moya自带的Rx进行网络请求如下:
typealias Complete = ([Model]) -> Void funcgetCatrgories(complete: @escaping Complete) { NewsProvider.request(.category) .subscribe({ event in switch event { let json = JSON(data: value.data) let jsonArray = json["data"]["data"] for (_, subJson):(String, JSON) in jsonArray { let model = Mapper<Model>().map(JSON: subJson.object as! [String : Any])! self.models.append(model) } complete(self.models) }) }
然后ViewController中请求如下:
viewModel.getCatrgories { [unowned self] (models) in self.models = models self.tableView.reloadData() }
这样子一个请求就差不多了,可能感觉也还不错,但是有没有感觉ViewModel中的那个代码像翔一样看着不顺心?什么JSON解析、转为Model、Model转换完毕要做啥等等都在一起,代码看着好不爽,如果按照RxSwift的思想来看,ViewController应该也是一个订阅者,他来决定最后他所关注的数据怎么使用。
首先我们建立一个扩展文件,我们对Moya的Response和ObservableType进行RxSwift的扩展:
extensionResponse{ // 这一个主要是将JSON解析为单个的Model public funcmapObject<T: BaseMappable>(_type: T.Type) throws -> T { guard let object = Mapper<T>().map(JSONObject: try mapJSON()) else { throw MoyaError.jsonMapping(self) } return object } // 这个主要是将JSON解析成多个Model并返回一个数组,不同的json格式写法不相同 public funcmapArray<T: BaseMappable>(_type: T.Type) throws -> [T] { let json = JSON(data: self.data) let jsonArray = json["data"]["data"] guard let array = jsonArray.arrayObject as? [[String: Any]], let objects = Mapper<T>().mapArray(JSONArray: array) else { throw MoyaError.jsonMapping(self) } return objects } } extensionObservableTypewhereE==Response{ // 这个是将JSON解析为Observable类型的Model public funcmapObject<T: BaseMappable>(_type: T.Type) -> Observable<T> { return flatMap { response -> Observable<T> in return Observable.just(try response.mapObject(T.self)) } } // 这个是将JSON解析为Observable类型的[Model] public funcmapArray<T: BaseMappable>(_type: T.Type) -> Observable<[T]> { return flatMap { response -> Observable<[T]> in return Observable.just(try response.mapArray(T.self)) } } }
这样子我们就可以对ViewModel中的getCategories方法进行变化了:
funcgetCategories() -> Observable<[Model]> { return NewsProvider .request(.category) // 请求 .mapArray(Model.self) // 请求的数据进行解析Model }
然后ViewController中的写法为:
viewModel.getCategories() .subscribe({ [unowned self] event in switch event { case .next(let models): self.models = models self.tableView.reloadData() case .error(let error): print(error) case .completed: return } }) .addDisposableTo(disposeBag)
这样就变成了订阅是发生在ViewController中的了,符合函数式响应编程的思想。是不是这样子更加清晰优雅了,双手奉上 demo
小伙伴们如果感觉文章可以,可以关注博主博客
小伙伴们多多关注博主微博,探索博主内心世界
如要转载请注明出处。