Rx Swift 네트워크 요청

설명
RxSwift 에 들 어간 지 오래 되 었 습 니 다.그 전에 프로젝트 에서 RxSwift 를 작은 범위 에서 만 사 용 했 습 니 다.응답 식 프로 그래 밍 을 잘 사용 하기 위해 프로젝트 에서 RxSwift 를 더욱 넓 게 사용 하기 로 결 정 했 습 니 다.그리고 RxSwift 의 네트워크 요청 을 연 구 했 습 니 다.현재 네트워크 요청 과 관련 된 사례 는 대부분 RXSwift(4.0.0)또는 더 빠 른 라 이브 러 리 를 바탕 으로 작 성 된 것 입 니 다.이 글 은 현재 최신 버 전(4.2.0)버 전 을 바탕 으로 쓴 것 으로 Rx Swift 버 전의 업데이트 로 인해 그 안의 문법 사용 에 변화 가 생 겼 고 정리 하 는 과정 에서 문제 가 생 겼 습 니 다.나중에 배 운 친구 들 이 시간 을 절약 할 수 있 도록 기록 하기 로 결 정 했 습 니 다.
네트워크 요청
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 에 대해 더욱 추상 적 인 패키지 입 니 다.Rx Swift 는 Moya 를 패키지 한 후에 네트워크 요청 의 인터페이스 로 사용 합 니 다.저 희 는 사용 할 때 Target Type 프로 토 콜 만 실현 하면 됩 니 다.예 를 들 어 어떻게 사용 하 는 지 보 겠 습 니 다.
    
    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:네트워크 요청 을 수행 하 는 작업
  • vaidationType:Alamofire 인증 을 실행 할 지 여부 입 니 다.기본 값 은 false
  • 입 니 다.
  • headers:네트워크 요청 에 필요 한 header 입 니 다.배경 과 특별한 검증 처리 가 없 으 면 기본 전송 nil
  • APIService 는 네트워크 요청 의 통 일 된 인터페이스 로 네트워크 요청 에 필요 한 기본 데이터
  • 를 밀봉 했다.
    3.네트워크 요청 을 하기 전에 네트워크 에서 요청 한 데 이 터 를 JSON 을 통 해 Model 로 전환 하 는 준 비 를 해 야 합 니 다.여기 서 우 리 는 두 가지 방식 으로 전환(프로젝트 의 상황 에 따라 유연 하 게 선택 하여 사용)했 습 니 다.하 나 는 Object Mapper 라 이브 러 리 를 통 해 전환 되 었 고 하 나 는 HandyJSON 라 이브 러 리 를 통 해 전환 되 었 으 며 각각 Response 류 를 통 해 확장 되 었 습 니 다.다음은 이 두 가지 방식 에 대한 포장 이다.
    첫째,Object Mapper 라 이브 러 리 를 사용 하여 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))
        }
      }
    }
    
    
    둘 째 는 핸 디 JSON 라 이브 러 리 로 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.MainClassView Model 에서 봉 인 된 인 터 페 이 스 를 사용 하여 네트워크 요청 을 합 니 다.코드 는 다음 과 같 습 니 다.
    
    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 인터페이스 에 대해 네트워크 요청 을 했 습 니 다.유일 하 게 다른 것 은 네트워크 에서 데 이 터 를 요청 할 때 하 나 는 mapObject Model 을 사용 하여 JSON 을 Model 로 바 꾸 는 것 입 니 다.하 나 는 mapHandy JSonModel 을 사용 하여 JSON 을 Model 로 바 꾸 는 것 입 니 다.우 리 는 서로 다른 라 이브 러 리 를 사용 하기 때문에 JSON 을 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 를 사용 하여 네트워크 요청 을 분석 한 것 입 니 다.다음 에 예 를 들 어 어떻게 사용 하 는 지 보 겠 습 니 다.MainClassView Model 에서 저 희 는 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)
    
    사용 할 곳 에서 호출 방법 getClassList With MapObject()또는 getClassList With MapHandy JSon()
    3.총화
    이 부분의 내용 은 RxSwift 에 대해 어느 정도 알 고 있 는 파트너 학습 에 적합 합 니 다.글 의 중점 은 RxSwift 네트워크 요청 에 관 한 지식 을 배우 고 이해 하 는 데 도움 을 주 는 것 입 니 다.다음은 demo 입 니 다.
    demo
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기