浅谈RxSwift 网络请求
一、说明
入坑rxswift 有段时间了,之前在项目中只是小范围的使用rxswift,为了更好的使用响应式编程,决定在项目中更广范围的使用rxswift,然后研究了一下rxswift的网络请求,现在有关网络请求的案例大多是基于rxswift(4.0.0)或者更早的库来写的,本篇文章是基于目前最新的版本(4.2.0)版本来写的,由于rxswift 版本的更新,里面的使用语法,发生了变化,在整理的过程中遇到了一些问题,为了让后来学习的小伙伴,节约时间,决定记录下来
二、网络请求
1.使用rxswift相关库的版本
- objectmapper (3.2.0)
- handyjson (4.1.1)
- moya (11.0.2)
- rxcocoa (4.2.0)
- rxswift (4.2.0)
2.在swift语言中,我们使用alamofire 作为网络库,moya 是对alamofire 更抽象一层的封装,rxswift把moya封装后作为网络请求的接口,我们在使用的时候只需要实现 targettype 协议就好,用一个例子来看下怎么使用:
import foundation import moya enum apiservice{ case mainclasslist } extension apiservice:targettype{ var baseurl: url { return url(string:"http://cmsadmin.fotoable.net")! } var path: string { switch self { case .mainclasslist: return "/sandboxcolor/category" } } var method: moya.method { switch self { case .mainclasslist: return .get } } var parameters: [string : any]? { switch self { case .mainclasslist: return nil } } var parameterencoding: parameterencoding { return urlencoding.default } var sampledata: data { return "{}".data(using: string.encoding.utf8)! } var task: task { return .requestplain } var headers: [string : string]? { return nil } }
首先,我们定义了一个 枚举 apiservice ,作用主要是在内部定义网络请求的接口,然后,就是对协议 targettype进行扩展,我们一一解读下里面的参数
- baseurl:网络请求的基本url
- path:用于匹配具体网络请求接口
- method:网络请求方式,常用就是 get/post 两种
- parameters:接口请求时要带的参数
- parameterencoding:参数编码方式(这里使用url的默认方式)
- sampledata:这里用于单元测试
- task:执行网络请求的任务
- validationtype:是否执行alamofire验证,默认值为false
- headers:网络请求时需要的header,如果和后台没有特殊的验证处理,默认传nil 就可以
- apiservice 作为网络请求的统一接口,里面封装了网络请求所需的一些基本数据
3.在进行网络请求之前,需要做一些准备工作,把网络请求回的数据通过json 转化成 model , 这里我们使用了两种方式进行转换(根据项目的情况,灵活选择使用),一种通过 objectmapper库进行转换,一种是通过 handyjson 库 进行转换 ,分别通过对 response 类 扩展 ,以下是对这两种方式的封装
其一:使用 objectmapper库 把json 转换成 model
import foundation import rxswift import moya import objectmapper // mark: - json -> model extension response { func mapobjectmodel<t: basemappable>(_ type: t.type, context: mapcontext? = nil) throws -> t { guard let object = mapper<t>(context: context).map(jsonobject: try mapjson()) else { throw moyaerror.jsonmapping(self) } return object } func mapobjectarray<t: basemappable>(_ type: t.type, context: mapcontext? = nil) throws -> [t] { guard let array = try mapjson() as? [[string : any]] else { throw moyaerror.jsonmapping(self) } return mapper<t>(context: context).maparray(jsonarray: array) } } // mark: - json -> observable<model> extension observabletype where e == response { // 将json解析为observable<model> public func mapobjectmodel<t: basemappable>(_ type: t.type) -> observable<t> { return flatmap { response -> observable<t> in return observable.just(try response.mapobjectmodel(t.self)) } } // 将json解析为observable<[model]> public func mapobjectarray<t: basemappable>(_ type: t.type) -> observable<[t]> { return flatmap { response -> observable<[t]> in return observable.just(try response.mapobjectarray(t.self)) } } }
其二 : 使用 handyjson 库 把json 转化成 model
import foundation import rxswift import moya import handyjson extension observabletype where e == response { public func maphandyjsonmodel<t: handyjson>(_ type: t.type) -> observable<t> { return flatmap { response -> observable<t> in return observable.just(response.maphandyjsonmodel(t.self)) } } } extension response { func maphandyjsonmodel<t: handyjson>(_ type: t.type) -> t { let jsonstring = string.init(data: data, encoding: .utf8) if let modelt = jsondeserializer<t>.deserializefrom(json: jsonstring) { return modelt } return jsondeserializer<t>.deserializefrom(json: "{\"msg\":\"请求有误\"}")! } }
4.在mainclassviewmodel中,使用已经封装好的接口进行网络请求,代码如下:
import rxswift import moya import objectmapper import handyjson import rxcocoa class mainclassviewmodel { private let provider = moyaprovider<apiservice>() let disposebag = disposebag() var datasource = behaviorrelay<[mainclassmodelmapobject_sub]>(value:[]) var networkerror = behaviorrelay(value: error.self) } //mark: -- 网络 extension mainclassviewmodel { //网络请求-- objectmapper func getclasslistwithmapobject(){ provider.rx.request(.mainclasslist).asobservable().mapobjectmodel(mainclassmodelmapobject.self).subscribe({ [unowned self] (event) in switch event { case let .next(classmodel): print("objectmapper -- 加载网络成功") self.datasource.accept(classmodel.data) case let .error( error): print("error:", error) self.networkerror.accept(error as! error.protocol) case .completed: break } }).disposed(by: self.disposebag) } //网络请求-- handyjson func getclasslistwithmaphandyjson(){ provider.rx.request(.mainclasslist).asobservable().maphandyjsonmodel(mainclassmodel.self).subscribe({ [unowned self] (event) in switch event { case let .next(classmodel): print("handyjson -- 加载网络成功") case let .error( error): print("error:", error) self.networkerror.accept(error as! error.protocol) case .completed: break } }).disposed(by: self.disposebag) } }
这里用了两种方式,分别对 mainclasslist api 接口进行了网络请求,唯一不同的是,在得到到网络请求回来数据的时候,一个是使用 mapobjectmodel 把json 转化成 model ,一个是使用 maphandyjsonmodel 把 json转化成model ,由于我们使用的是不同的库,把json 转化成 model,这两种实现的方式还是有一些差别,下面是这两种 model 的具体实现方式:
其一、实现协议 mappable
import uikit import objectmapper class mainclassmodelmapobject: mappable { var code:nsinteger? var data:[mainclassmodelmapobject_sub]! required init?(map: map) {} func mapping(map: map) { code <- map["code"] data <- map["data"] } } class mainclassmodelmapobject_sub: mappable { var id:string? var name:string? var desc:string? var imgurl:string? var gifurl:string? var isupdate:bool? var backgroundgroup:nsinteger? required init?(map: map) {} func mapping(map: map) { id <- map["id"] name <- map["name"] desc <- map["desc"] imgurl <- map["imgurl"] gifurl <- map["gifurl"] isupdate <- map["isupdate"] backgroundgroup <- map["backgroundgroup"] } }
其二、实现协议 handyjson
import uikit import handyjson struct mainclassmodel: handyjson { var code:nsinteger? var data:[mainclassmodel_sub]! } struct mainclassmodel_sub: handyjson { var id:string? var name:string? var desc:string? var imgurl:string? var gifurl:string? var isupdate:bool? var backgroundgroup:nsinteger? }
5、以上是使用 rxswift 进行网络请求的分析,接下来看一个示例如何使用,在mainclassviewmodel 中我们使用 datasource 保存了网络请求回来的数据,我们要在 viewcontroller里 用tableview 把这个数据展示出来,需要提前把数据源和tableview进行绑定,以下是示例代码:
//cell viewmodel.datasource.bind(to: tableview.rx.items) { (tableview, row, element) in let cell = tableview.dequeuereusablecell(withidentifier: "mainclasstableviewcell", for: indexpath(row: row, section: 0)) as! mainclasstableviewcell cell.setmodel(model: element) // configure cell return cell } .disposed(by: disposebag)
在需要使用的地方,调用 方法 getclasslistwithmapobject() 或者 getclasslistwithmaphandyjson()
三、总结
这部分的内容,适合对rxswift 有一定了解的小伙伴学习, 文章重点是 帮助大家学习和了解 rxswift 网络请求的相关知识,下面是一个写好的demo
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。