我博客中前面的文章详细地介绍过了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的仓库:
class Model: Mappable {
var category: String?
var name: String?
required init?(map: Map) {
}
func mapping(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() //使用RxSwift扩展建立Provider
public enum NewsService {
case category
}
extension NewsService: 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
func getCatrgories(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().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应该也是一个订阅者,他来决定最后他所关注的数据怎么使用。
优雅的请求
下面我们使用RxSwift把JSON解析的那些代码进行分离
首先我们建立一个扩展文件,我们对Moya的Response和ObservableType进行RxSwift的扩展:
extension Response {
// 这一个主要是将JSON解析为单个的Model
public func mapObject(_ type: T.Type) throws -> T {
guard let object = Mapper().map(JSONObject: try mapJSON()) else {
throw MoyaError.jsonMapping(self)
}
return object
}
// 这个主要是将JSON解析成多个Model并返回一个数组,不同的json格式写法不相同
public func mapArray(_ 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().mapArray(JSONArray: array) else {
throw MoyaError.jsonMapping(self)
}
return objects
}
}
extension ObservableType where E == Response {
// 这个是将JSON解析为Observable类型的Model
public func mapObject(_ type: T.Type) -> Observable {
return flatMap { response -> Observable in
return Observable.just(try response.mapObject(T.self))
}
}
// 这个是将JSON解析为Observable类型的[Model]
public func mapArray(_ type: T.Type) -> Observable<[T]> {
return flatMap { response -> Observable<[T]> in
return Observable.just(try response.mapArray(T.self))
}
}
}
这样子我们就可以对ViewModel中的getCategories方法进行变化了:
func getCategories() -> 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
小伙伴们如果感觉文章可以,可以关注博主博客
小伙伴们多多关注博主微博,探索博主内心世界
如要转载请注明出处。