SoFunction
Updated on 2025-04-10

A brief discussion on RxSwift network requests

1. Explanation

RxSwift has been in the pit for a while. Before, I only used RxSwift in the project for a small scale. In order to better use responsive programming, I decided to use RxSwift in the project for a wider range. Then I studied RxSwift's network requests. Now most of the cases about network requests are based on RXSwift (4.0.0) or earlier libraries. This article is based on the latest version (4.2.0) version. Due to the update of the RxSwift version, the syntax in it has changed. I encountered some problems during the collation process. In order to save time for my friends who later learned to record it.

2. Network request

1. Use the version of RxSwift related libraries

  • ObjectMapper (3.2.0)
  • HandyJSON (4.1.1)
  • Moya (11.0.2)
  • RxCocoa (4.2.0)
  • RxSwift (4.2.0)

2. In the Swift language, we use Alamofire as the network library. moya is a more abstract layer of Alamofire encapsulation. RxSwift encapsulates Moya as an interface for network requests. When we use it, we only need to implement the TargetType protocol. Use an example to see how to use it:

import Foundation
import Moya
enum APIService{
  case mainClassList
}

extension APIService:TargetType{

  var baseURL: URL {
    return URL(string:"")!
  }
  
  var path: String {
    switch self {
    case .mainClassList:
       return "/sandboxColor/category"
    }
  }
  
  var method:  {
    switch self {
    case .mainClassList:
       return .get
    }
  }
  
  var parameters: [String : Any]? {
    
    switch self {
    case .mainClassList:
      return nil
    }
  }
  
  var parameterEncoding: ParameterEncoding {
    
    return 
  }
  
  var sampleData: Data {
    return "{}".data(using: .utf8)!
  }
  
  var task: Task {
    return .requestPlain
  }
  
  var headers: [String : String]? {
    return nil
  }
}

First, we define an enumeration APIService, which mainly defines the interface for network requests internally. Then, it extends the protocol TargetType. We interpret the parameters one by one.

  • baseURL: The basic URL for network requests
  • path: used to match specific network request interface
  • method: The network request method is commonly used to get/post.
  • parameters: parameters to be brought when requesting the interface
  • parameterEncoding: parameter encoding method (the default method of using URL here)
  • sampleData: This is used for unit testing
  • task: perform network requested tasks
  • validationType: Whether to perform Alamofire verification, the default value is false
  • headers: The header required during network requests. If there is no special verification process with the background, the default is to pass nil
  • APIService is a unified interface for network requests, encapsulates some basic data required for network requests.

3. Before making a network request, you need to do some preparation work to convert the data requested by the network into Model through JSON. Here we use two methods to convert (flexible choice to use according to the project situation). One is to convert it through the ObjectMapper library, and the other is to convert it through the HandyJSON library, respectively, and extend the Response class. The following is the encapsulation of these two methods.

One: Use the ObjectMapper library to convert JSON to Model

import Foundation
import RxSwift
import Moya
import ObjectMapper

// MARK: - Json -> Model
extension Response {
  
  func mapObjectModel<T: BaseMappable>(_ type: , context: MapContext? = nil) throws -> T {
    guard let object = Mapper<T>(context: context).map(JSONObject: try mapJSON()) else {
      throw (self)
    }
    return object
  }
  
  func mapObjectArray<T: BaseMappable>(_ type: , context: MapContext? = nil) throws -> [T] {
    guard let array = try mapJSON() as? [[String : Any]] else {
      throw (self)
    }
    return Mapper<T>(context: context).mapArray(JSONArray: array)
  }
}

// MARK: - Json -> Observable<Model>

extension ObservableType where E == Response {
  // parse Json to Observable<Model>  public func mapObjectModel&lt;T: BaseMappable&gt;(_ type: ) -&gt; Observable&lt;T&gt; {
    return flatMap { response -&gt; Observable&lt;T&gt; in
      return (try ())
    }
  }
  // parse Json to Observable<[Model]>  public func mapObjectArray&lt;T: BaseMappable&gt;(_ type: ) -&gt; Observable&lt;[T]&gt; {
    return flatMap { response -&gt; Observable&lt;[T]&gt; in
      return (try ())
    }
  }
}

Second: Use the HandyJSON library to convert JSON into Model

import Foundation
import RxSwift
import Moya
import HandyJSON

extension ObservableType where E == Response {
  public func mapHandyJsonModel&lt;T: HandyJSON&gt;(_ type: ) -&gt; Observable&lt;T&gt; {
    return flatMap { response -&gt; Observable&lt;T&gt; in
      return (())
    }
  }
}

extension Response {
  func mapHandyJsonModel&lt;T: HandyJSON&gt;(_ type: ) -&gt; T {
    let jsonString = (data: data, encoding: .utf8)
    if let modelT = JSONDeserializer&lt;T&gt;.deserializeFrom(json: jsonString) {
      return modelT
    }
    return JSONDeserializer&lt;T&gt;.deserializeFrom(json: "{\"msg\":\"The request was wrong\"}")!
  }
}

4. In MainClassViewModel, use the encapsulated interface to make network requests, the code is as follows:

import RxSwift
import Moya
import ObjectMapper
import HandyJSON
import RxCocoa

class MainClassViewModel {

  private let provider = MoyaProvider&lt;APIService&gt;()
  let disposeBag = DisposeBag()
  var dataSource = BehaviorRelay&lt;[MainClassModelMapObject_sub]&gt;(value:[])
  var networkError = BehaviorRelay(value: )
}


//MARK: -- Networkextension MainClassViewModel {
  
  //Network request--ObjectMapper  func getClassListWithMapObject(){
    (.mainClassList).asObservable().mapObjectModel().subscribe({ [unowned self] (event) in
      
      switch event {
      case let .next(classModel):
        print("ObjectMapper -- Loading the network successfully")
        ()
        
      case let .error( error):
        print("error:", error)
        (error as! )
      case .completed: break
      }
    }).disposed(by: )
  }
  
  
  //Network request-- HandyJSON  func getClassListWithMapHandyJson(){
    (.mainClassList).asObservable().mapHandyJsonModel().subscribe({ [unowned self] (event) in
      
      switch event {
      case let .next(classModel):
        
        print("HandyJSON -- Loading the network successfully")
        
      case let .error( error):
        print("error:", error)
        (error as! )
      case .completed: break
      }
    }).disposed(by: )
  }
  
}

There are two methods here, which respectively make network requests for the mainClassList API interface. The only difference is that when you get the data back from the network request, one is to convert JSON into Model using mapObjectModel, and the other is to convert JSON into Model using mapHandyJsonModel. Since we use different libraries, we convert JSON into Model. There are still some differences in the implementation methods of these two models. Here are the specific implementation methods of these two models:

First, implement the protocol 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"]
  }
}

Second, implement the protocol 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. The above is an analysis of network requests using RxSwift. Let’s take a look at how to use an example. In MainClassViewModel, we use dataSource to save the data returned from network requests. We want to display this data using tableview in the ViewController. We need to bind the data source and TableView in advance. The following is the sample code:

 //cell
   (to: ) { (tableView, row, element) in
      let cell = (withIdentifier: "MainClassTableViewCell", for: IndexPath(row: row, section: 0)) as! MainClassTableViewCell
      
      (model: element)
      // configure cell
      return cell
      }
      .disposed(by: disposeBag)

Where needed, call the method getClassListWithMapObject() or getClassListWithMapHandyJson()

3. Summary

This part of the content is suitable for those who have a certain understanding of RxSwift. The focus of the article is to help everyone learn and understand the relevant knowledge of RxSwift network requests. The following is a written demo.

demo

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.