관리 메뉴

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

[RxSwift] 7.Transforming Operators 본문

RxSwift/RxSwift 기본

[RxSwift] 7.Transforming Operators

jake-kim 2020. 6. 1. 16:45

1. Transforming elements

1) toArray

- 개별 원소들 -> 하나의 배열로

 

example(of: "toArray") {
  let disposeBag = DisposeBag()

  Observable.of("A", "B", "C")

    .toArray()
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
}

// prints : ["A", "B", "C"]

2) map

이벤트를 mapping하여 주어진 식을 통해 새로 변경

example(of: "map") {
  let disposeBag = DisposeBag()

  // 1
  let formatter = NumberFormatter()
  formatter.numberStyle = .spellOut

  // 2
  Observable<Int>.of(123, 4, 56)
    // 3
    .map {
      formatter.string(for: $0) ?? ""
    }
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
}

 

3) enumerated와 map

example(of: "enumerated and map") {
  let disposeBag = DisposeBag()

  // 1
  Observable.of(1, 2, 3, 4, 5, 6)
    // 2
    .enumerated()
    // 3
    .map { index, integer in
      index > 2 ? integer * 2 : integer
    }
    // 4
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
}
/* prints
 1
 2
 3
 8
 10
 12
 */

2. Transforming inner observables

1) flatMap

각 하나의 sequence에서 각 event에 대해 sequence를 만든 후,

최종적으로 하나의 sequence로 만드는 것

- 1번 : 01엘리먼트가 flatMap을 만나 새로운 시퀀스 생성, 최종 시퀀스로 전달

- 2번 : 02엘리먼트가 flatMap을 만나 새로운 시퀀스 생성, 최종 시퀀스로 전달

- 3번 : 01엘리먼트의 value에 변동이 생김(10 -> 40), 최종 시퀀스로 전달

- 4번 : 02엘리먼트의 value에 변동이 생김(20 -> 50), 최종 시퀀스로 전달

flatMap

//
struct Student {
    var score: BehaviorSubject<Int>
}

 example(of: "flatMap") {
     let disposeBag = DisposeBag()
     
     // 1
     let ryan = Student(score: BehaviorSubject(value: 80))
     let charlotte = Student(score: BehaviorSubject(value: 90))
     
     // 2
     let student = PublishSubject<Student>()
     
     // 3
     student
         .flatMap{
             $0.score
         }
         // 4
         .subscribe(onNext: {
             print($0)
         })
         .disposed(by: disposeBag)
     
     // 5
     student.onNext(ryan)    // Print: 80
     
     // 6
     ryan.score.onNext(85)   // Print: 80 85
     
     // 7
     student.onNext(charlotte)   // Print: 80 85 90
     
     // 8
     ryan.score.onNext(95)   // Print: 80 85 90 95
     
     // 9
     charlotte.score.onNext(100) // Print: 80 85 90 95 100
 }

 * 주석

 1. 두 학생 객체 생성, 각 score를 갖는 BehaviorSubject객체로 초기화

 2. 바로 전의 두 학생 객체의 score에 대한 이벤트를 처리하기 위해 PublishSubject객체 생성

 3. student시퀀스를 score값을 기준으로 flatMap 변환

 4. 변환된 시퀀스에 구독 요청 및 이벤트 print()

 

* map vs flatMap

  map flatMap
변경 :Observable<Int>.just(10)
-> Observable<String>.just(10)
이벤트를 가지고 변경
String($0)
새로운 Observable를 생성하여 변경
Observable<String>.just(String($0))
접근 클로저에 연산 값 리턴
let n = Observable<Int>.just(10)
let m = n.map{ String($0) }
Observable리턴
let n = Observable<Int>.just(10)
let fm = n.map{Observable<String>.just(String($0)}

 

2) flatMapLatest

- 가장 최근에 만들어진 sequence만 emit

- 1번 : 01엘리먼트가 flatMap을 만나서 새로운 시퀀스 생성, 최종 시퀀스에 전달

- 2번 : 동일, 바로 뒤 30은 전달이 안되는 이유는 가장 마지막에 생성된 시퀀스는 20시퀀스이므로 20시퀀스에 대해서만 반응

- 3번 : 동일, 가장 마지막에 생성된 시퀀스는 40시퀀스이므로 뒤에 60도 최종 시퀀스에 전달

 

   (그 당시 최근의 sequence는 1번으로 만들어진 것이 아닌, 2번의 sequence이므로)

example(of: "flatMapLatest") {
  let disposeBag = DisposeBag()

  let laura = Student(score: BehaviorSubject(value: 80))
  let charlotte = Student(score: BehaviorSubject(value: 90))

  let student = PublishSubject<Student>()

  student
    .flatMapLatest {
      $0.score
    }
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)

  student.onNext(laura) // print : 80
  laura.score.onNext(85) // print : 85
  student.onNext(charlotte) // print : 90

  laura.score.onNext(95) // print : none
  charlotte.score.onNext(100) // print : 100
}

3. Observing events

- materialize 연산자 : 이벤트 타입으로 변환 후, Obervable 형으로 나타내는 것 Observable<Event<Int>>

- 용도 : elements가 아닌, events(onNext, onError, onCompleted)에서 이벤트를 처리하는 연산자

materialize

1) materialize를 쓰지 않은 경우

<이벤트 정의, 필요한 객체 생성>

example(of: "materialize and dematerialize") {

  // 이벤트 정의
  enum MyError: Error {
    case anError
  }

  let disposeBag = DisposeBag()
  
  let laura = Student(score: BehaviorSubject(value: 80))
  let charlotte = Student(score: BehaviorSubject(value: 100))
  
  let student = BehaviorSubject(value: laura)
}

 

<onError이벤트에 대한 따로 처리가 없기 때문에 중간에 종료>

example(of: "materialize and dematerialize") {
  // 1
  enum MyError: Error {
    case anError
  }

  let disposeBag = DisposeBag()

  // 2
  let laura = Student(score: BehaviorSubject(value: 80))
  let charlotte = Student(score: BehaviorSubject(value: 100))
  
  let student = BehaviorSubject(value: laura)
    
    let studentScore = student
      .flatMapLatest {
        $0.score
      }

    studentScore
      .subscribe(onNext: {
        print($0) // print : 80
      })
      .disposed(by: disposeBag)

      laura.score.onNext(85) // print : 85
      laura.score.onError(MyError.anError) // print : Unhandled error happened: anError
      laura.score.onNext(90) // print : none
      student.onNext(charlotte) // print : none
}

 

2) materlize 사용

- next이벤트(Int타입)를 Event타입으로 변환

- onError이벤트를 Event타입으로 변환

- onCompleted이벤트를 Event타입으로 변환

// (... 중략 ...)

let studentScore = student
  .flatMapLatest {
    $0.score.materialize()
  }
  
// (... 하략 ...)

/* prints
  next(80)
  next(85)
  error(anError)
  next(100)
*/

 

Comments