浅谈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

demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • RxSwift学习教程之基础篇

    前言 我们在 iOS 开发过程中,几乎无时无刻都要面对异步事件的处理.例如,按键点击.数据保存..音频后台播放.交互动画展示.这些事件并不具备特定时序性,甚至它们可能同时发生. 虽然 Apple 提供了通知.代理.GCD.闭包等异步机制,但是这些机制缺乏一个统一的抽象表述.另外,这些机制在处理共享的可变数据或状态时不够清晰简练.当然,这并不是说编写优雅的异步代码不现实.毕竟与其他平台相比 iOS 的异步机制还是很强大的. 幸运的是,我们能够通过 RxSwift 优雅的处理异步代码. 至于 RxS

  • RxSwift学习教程之类型对象Subject详解

    前言 在上一篇文章我们介绍了 Observable 的基本概念和使用情形.但是大多数情形下,我们需要在应用运行时添加数据到 Observable 中并将其发送给订阅者.在这种需求场景下,我们就不得不使用 RxSwift 中另一种类型对象了 - Subject . 在应用中 Subject 实际上同时扮演了两个不同的角色:既是可观察对象同时也是观察者.这意味着 Subject 实例对象既可以接收事件也可以发送事件.例如,Subject 实例对象可以接收 next 事件信息,然后再将其发送给它自己的

  • RxSwift学习之Observable的新建、订阅及取消订阅

    前言 我们在前一篇基础之上,本文将会介绍 RxSwift 中的 Observables 部分. 在 RxSwift 中 Observable 也被称为 Observable Sequence.Sequence.Stream.Observable 会以异步的方式不断的发射事件形成事件流,并且数据也会沿着事件流进行传播.下图是事件流的图像化表示: 其中从左到右的箭头代表时间轴,而三个圆圈则构成了可观察序列.而整个过程会按照从左到右的顺序.另外,事件可能在可观察序列生命周期内的任意时刻被触发. Obs

  • RxSwift使用技巧之过滤操作详解

    前言 在前面的基础之上接下来我会介绍一些常用的函数和实用技巧.首先,本文将会介绍那些用于对 next 事件进行过滤的操作.这些过滤操作类似于 Swift 标准库中的 filter 操作.它能在我们开始真正进行业务处理前先把那些不符合条件的过滤掉,而且这种函数式编程的范式也能开阔我们的思维. Ignore 过滤 RxSwift 中最简单直接的过滤操作就是 ignoreElements 了.该操作会屏蔽所有的 next 事件,只会将注意力放在 error 和 completed 事件上.如下图所示,

  • 浅谈RxSwift 网络请求

    一.说明 入坑RxSwift 有段时间了,之前在项目中只是小范围的使用RxSwift,为了更好的使用响应式编程,决定在项目中更广范围的使用RxSwift,然后研究了一下RxSwift的网络请求,现在有关网络请求的案例大多是基于RXSwift(4.0.0)或者更早的库来写的,本篇文章是基于目前最新的版本(4.2.0)版本来写的,由于RxSwift 版本的更新,里面的使用语法,发生了变化,在整理的过程中遇到了一些问题,为了让后来学习的小伙伴,节约时间,决定记录下来 二.网络请求 1.使用RxSwif

  • 浅谈Vue网络请求之interceptors实际应用

    项目背景 最近在项目开发中,遇到下面这样一个问题: 在进行铭感操作之前,每个请求需要携带token,但是token 有有效期,token 失效后需要换取新的token并继续请求. 需求分析 每个请求都需要携带 token ,所以我们可以使用 axios request 拦截器,在这里,我们给每个请求都加 token,这样就可以节省每个请求再一次次的复制粘贴代码. token 失效问题,当我们token 失效,我们服务端会返回一个特定的错误表示,比如 token invalid,但是我们不能在每个

  • 浅谈django url请求与数据库连接池的共享问题

    但凡介绍数据库连接池的文章,都会说"数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正是针对这个问题提出来的.数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个:释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏.这项技术能明显提高对数据库操作的性能." 这句

  • 浅谈Java HttpURLConnection请求方式

    一)URL代理请求 ​ 该方式请求有两种代理方式. 方式一:使用该方式代理之后,之后的所有接口都会使用代理请求 // 对http开启全局代理 System.setProperty("http.proxyHost", "192.168.1.1"); System.setProperty("http.proxyPort", "80"); // 对https开启全局代理 System.setProperty("https.

  • 浅谈Okhttp去除请求头user-agent

    Http Header之User-Agent  User-Agent中文名为用户代理,是Http协议中的一部分,属于头域的组成部分,User Agent页简称UA.她是一个特殊字符串头,是一种想访问网站提供你说使用的浏览器类型和版本,操作系统和版本,浏览器内核等信息的标识,用户所访问的网站可以显示不同的排版,而为用户提供更好的体验或者进行信息统计 现象说明 用OKhttp框架请求http请求的时候会把user-agent带上:然而有些时候我们需要把请求头里面的user-agent去掉: 客户端程

  • 浅谈C#网络编程详解篇

    阅读目录: 基础 Socket编程 多线程并发 阻塞式同步IO 基础 在现今软件开发中,网络编程是非常重要的一部分,本文简要介绍下网络编程的概念和实践. Socket是一种网络编程接口,它是对传输层TCP.UDP通信协议的一层封装,通过友好的API暴露出去,方便在进程或多台机器间进行网络通信. Socket编程 在网络编程中分客户端和服务端两种角色,比如通过打开浏览器访问到挂在Web软件上的网页,从程序角度上来看,即客户端(浏览器)发起了一个Socket请求到服务器端,服务器把网页内容返回到浏览

  • 浅谈Linux 网络 I/O 模型简介(图文)

    1.介绍 Linux 的内核将所有外部设备都看做一个文件来操作(一切皆文件),对一个文件的读写操作会调用内核提供的系统命令,返回一个file descriptor(fd,文件描述符).而对一个socket的读写也会有响应的描述符,称为socket fd(socket文件描述符),描述符就是一个数字,指向内核中的一个结构体(文件路径,数据区等一些属性). 根据UNIX网络编程对I/O模型的分类,UNIX提供了5种I/O模型. 1.1.阻塞I/O模型 最常用的I/O模型,默认情况下,所有文件操作都是

  • 浅谈Scrapy网络爬虫框架的工作原理和数据采集

    今天小编给大家详细的讲解一下Scrapy爬虫框架,希望对大家的学习有帮助. 1.Scrapy爬虫框架 Scrapy是一个使用Python编程语言编写的爬虫框架,任何人都可以根据自己的需求进行修改,并且使用起来非常的方便.它可以应用在数据采集.数据挖掘.网络异常用户检测.存储数据等方面. Scrapy使用了Twisted异步网络库来处理网络通讯.整体架构大致如下图所示. 2.由上图可知Scrapy爬虫框架主要由5个部分组成,分别是:Scrapy Engine(Scrapy引擎),Scheduler

  • 浅谈docker-compose网络设置之networks

    networks使用方式之官网教程 官网的docker-compose.yml参考文档:Compose file version 3 reference 较为准确的中文翻译版:Compose file version 3 reference networks通常应用于集群服务,从而使得不同的应用程序得以在相同的网络中运行,从而解决网络隔离问题.这种应用在swarm部署中,非常常见.不过,本文并不做讨论. 一般对于集群服务,常常通过docker-compose.yml文档快速编排.部署应用服务.官

  • 浅谈IOS中AFNetworking网络请求的get和post步骤

    1.首先通过第三方:CocoaPods下载AFNetworking 1.1.先找到要查找的三方库:pod search + AFNetworking 1.2.出来一堆列表页面,选择三方库最新版本命令,例如: pod 'MBProgressHUD','~>0.8'  (:q 返回) 1.3.创建工程,进入工程: cd + 工程路径 1.4.编辑工程的Podfile文件: vim Podfile 1.5.(platform :iOS, '8.0'
target "工程名" do
po

随机推荐