转载

【iOS开发】老生常谈RxSwift+Moya进行优雅的网络请求

我博客中前面的文章详细地介绍过了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应该也是一个订阅者,他来决定最后他所关注的数据怎么使用。

优雅的请求

下面我们使用RxSwift把JSON解析的那些代码进行分离

首先我们建立一个扩展文件,我们对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

小伙伴们如果感觉文章可以,可以关注博主博客

小伙伴们多多关注博主微博,探索博主内心世界

如要转载请注明出处。

原文  http://www.codertian.com/2017/02/04/iOS-Moya-RxSwift-better-networking/
正文到此结束
Loading...