관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 1. RxSwift의 Map, FlatMap 사용하여 비동기를 순서대로 처리 방법 본문

RxSwift/RxSwift 응용

[iOS - swift] 1. RxSwift의 Map, FlatMap 사용하여 비동기를 순서대로 처리 방법

jake-kim 2022. 1. 31. 02:33

1. RxSwift의 Map, FlatMap - 사용하여 비동기를 순서대로 처리 방법

2. RxSwift의 Map, FlatMap - 사이드 이펙트 처리 방법 (throw와 catch 사용) 

확인 탭 > UILabel에 response 기록 > 3초후에 UIImage에 적용

편리를 위해 사용한 프레임워크

# UI
pod 'SnapKit'
pod 'Then'
# Rx
pod 'RxSwift'
pod 'RxCocoa'
# Network
pod 'Moya/RxSwift'
# Utils
pod 'JGProgressHUD' # 로딩

사용한 API - Unsplash

비동기 작업 순서

  1. `확인` 버튼 탭
  2. API를 통해서 이미지 url 획득, url을 UILabel에 입력
  3. 로딩 프로그래스 바 표출 후, url을 가지고 이미지를 획득
  4. 이미지 획득 후 3초 후에 UIImage에 이미지 적용

비동기 작업을 순서대로 처리하는 아이디어

  • Rx의 flatMap과 map을 적절히 사용하여 처리
    • API 서비스를 Observabe<SomeModel>을 리턴하도록 구현되어 있는 상태라면, 스트림에서 flatMap을 사용하여 데이터 획득
    • 위에서 얻어온 데이터를 Map 또는 do(onNext:)에서 처리하고난 후 적절한 값을 다음 스트림에 전달
  • 랜덤 이미지 url을 가져오는 API와 url을 가지고 UIImage를 가져오는 API 총 두개를 사용하므로 flatMap도 총 2개 사용 예정

Network

  • moya 사용하여 Endpoint 정의
//  UnsplashTargetType.swift

import Moya

enum UnsplashTargetType: TargetType {
  case getPhoto
}

extension UnsplashTargetType {
  var baseURL: URL {
    return URL(string: "https://api.unsplash.com/")!
  }
  
  var path: String {
    switch self {
    case .getPhoto:
      return "photos/random"
    }
  }
  
  var method: Method {
    switch self {
    case .getPhoto:
      return .get
    }
  }
  
  var task: Task {
    return .requestPlain
  }
  
  var headers: [String : String]? {
    return [
      "Content-Type": "application/json",
      "Authorization": "Client-ID A_zoeYvBtQoSlNw51lkUN0C0YOa3XhvuENeFvI0AIAk"
    ]
  }
}
  • 위 Endpoint와 Moya에서 제공하는 provider를 가지고 request하는 컴포넌트 정의
    • 단, request한 결과는 Observable 형태로 반환하도록 구현 (Observable를 리턴하면 사용하는 쪽에서 Rx 스트림으로 사용가능하기 때문)
// API.swift

import Moya
import RxSwift

private let provider = MoyaProvider<UnsplashTargetType>()

enum API {
  /// 랜덤 이미지 URL 획득
  static func getPhoto() -> Observable<Photo> {
    provider.rx.request(.getPhoto)
      .map(Photo.self)
      .asObservable()
      .do(onError: { print("Error = \($0)") })
  }
  
  /// url을 가지고 UIImage 얻어오는 함수
  static func getPhoto(url: String) -> Observable<UIImage> {
    guard
      let url = URL(string: url),
      let data = try? Data(contentsOf: url),
      let image = UIImage(data: data)
    else { return .empty() }
    return .just(image)
  }
}

사용하는 쪽

  • flatMap과 map을 적절히 사용
    • flatMap: Observable을 리턴해야하는 경우 사용하는데, API들은 Observable<MyType>을 리턴하게 되었으므로 사용
    • map: 위 flatMap에서 받아온 것중 MyType에 접근하여 특정 값을 처리하고, 다음 스트림에 필요한 값을 리턴해주는데 사용
// ViewController.swift

self.button.rx.tap
  .flatMap { API.getPhoto() }
  .map(\.urls.regular)
  .map { [weak self] in
    self?.label.text = $0
    return $0
  }
  .do(onNext: { [weak self] _ in self?.showLoading() })
  .flatMap { API.getPhoto(url: $0) }
  .delay(.seconds(3), scheduler: MainScheduler.asyncInstance)
  .do(onNext: { [weak self] _ in self?.hideLoading() })
  .bind(to: self.imageView.rx.image)
  .disposed(by: self.disposeBag)

 

* 전체 코드: https://github.com/JK0369/ExFlatMap

Comments