RxSwift 网络请求

作者: hylccmh | 来源:发表于2018-07-17 14:58 被阅读10次
    一、说明
    入坑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

    demo

    相关文章

      网友评论

        本文标题:RxSwift 网络请求

        本文链接:https://www.haomeiwen.com/subject/rnabpftx.html