본문 바로가기
iOS/RxSwift

[RxSwift] Transforming Operators

by 0inn 2022. 11. 17.

최근에 공부를 못하고 . . 프젝이랑 코테 CS만 하다 . . 다시 정신차립니다 . .

빠르게 남은 파트 정리하고 다른 공부도 해야죠 ! 하하

Transforming elements

 

toArray

 

Observable의 독립적인 요소들을 array로 넣는 방법

 

[코드]

 example(of: "toArray") {
 	let disposeBag = DisposeBag()
 	
 	// 1
 	Observable.of("A", "B", "C")
 		// 2
 		.toArray()
 		.subscribe(onNext: {
 			print($0)
 		})
 		.disposed(by: disposeBag)
 		
 	/* Prints:
 		["A", "B", "C"]
 	*/
 }

 

map

 

 

표준 라이브러리 map과 같이 사용되며 Observable에서 동작합니다.

 

enumerated

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)
}

/*
--- Example of: enumerated and map ---
1
2
3
8
10
12
*/

 

compactMap

 

nil을 제거하고 옵셔널 바인딩해주는 Swift 표준라이브러리 compactMap과 같이 동작합니다.

 

[코드]

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

  // 1
  Observable.of("To", "be", nil, "or", "not", "to", "be", nil)
    // 2
    .compactMap { $0 }
    // 3
    .toArray()
    // 4
    .map { $0.joined(separator: " ") }
    // 5
    .subscribe(onSuccess: {
      print($0)
    })
    .disposed(by: disposeBag)
}

/*
--- Example of: compactMap ---
To be or not to be
*/

Transforming inner observables

flatMap

 

flatMap을 "Observable sequence의 각 요소를 Observable sequence에 투영하고 Observable sequence를 Observable sequence로 병합합니다."라고 정의하고 있습니다.

뭔 뜻일까 . . ?

 

 

[코드]

 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)    // Printed: 80
     
     // 6
     ryan.score.onNext(85)   // Printed: 80 85
     
     // 7
     student.onNext(charlotte)   // Printed: 80 85 90
     
     // 8
     ryan.score.onNext(95)   // Printed: 80 85 90 95
     
     // 9
     charlotte.score.onNext(100) // Printed: 80 85 90 95 100
 }

즉, flatMap은 각 Observable의 변화를 계속 지켜봅니다.

 

flatMapLatest

 

flatMap에서 가장 최신의 값만 확인하고 싶을 때, flatMapLatest를 사용합니다.

flapMapLatest = map + switchLatest (switchLastest에 대해선 나중에 다뤄볼게요 . . !)

 

 

[코드]

 example(of: "flatMapLatest") {
     let disposeBag = DisposeBag()
     
     let ryan = 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(ryan)
     ryan.score.onNext(85)
     student.onNext(charlotte)
     
     // 1
     ryan.score.onNext(95)
     charlotte.score.onNext(100)
     
     /* Prints:
     	80 85 90 100
     */
 }

ryan.score.onNext(95)가 반영되지 않았습니다.

그 이유는 flatMapLatest가 이미 가장 최근 Observable인 charlotte로 전환했기 때문입니다.

 

언제 사용할까 ?

네트워킹 조작에서 가장 흔하게 쓰일 수 있습니다.

사전으로 단어를 찾는 경우를 생각해봅시다.

s, w, i, f, t를 입력하면 새 검색을 실행하고, 이전 검색(s, sw, swi, swif)을 무시해야할 때 사용할 수 있을 것입니다.

Observing events

observable을 observable 이벤트로 변환해야할 수 있습니다.

 

[코드]

 example(of: "materialize and dematerialize") {
     
     // 1
     enum MyError: Error {
         case anError
     }
     
     let disposeBag = DisposeBag()
     
     // 2
     let ryan = Student(score: BehaviorSubject(value: 80))
     let charlotte = Student(score: BehaviorSubject(value: 100))
     
     let student = BehaviorSubject(value: ryan)
     
     // 3
     let studentScore = student
         .flatMapLatest{
             $0.score
     }
     
     // 4
     studentScore
         .subscribe(onNext: {
             print($0)
         })
         .disposed(by: disposeBag)
     
     // 5
     ryan.score.onNext(85)
     ryan.score.onError(MyError.anError)
     ryan.score.onNext(90)
     
     // 6
     student.onNext(charlotte)
     
     /* Prints:
 		80 
 		85 
 		Unhandled error happened: anError
     */
 }

error가 studentScore의 종료를 발생시키기 때문에 student도 종료되므로 Error가 발생합니다.

이 때, materialize 연산자를 사용하면 각각 방출되는 이벤트를 이벤트의 observable로 만들 수 있습니다.

 

 

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

/*
--- Example of: materialize and dematerialize ---
next(80)
next(85)
error(anError)
next(100)
*/

위 코드에 materialize()를 추가하면 studentScore의 타입은 Observable<Event<Int>>인 것을 확인할 수 있습니다.

그리고 현재 방출하는 이벤트를 구독하게 됩니다.

에러는 여전히 studentScore의 종료를 발생시키지만, student observable은 그대로 살려놓게 됩니다.

그러므로 새로운 학생인 charlotte를 추가하였을 때, 해당 학생의 점수 100이 성공적으로 출력됩니다.

 

하지만, 이 경우에도 event만 받을 뿐 요소들을 받을 수 없습니다.

이를 해결하기 위해 dematerialize를 사용합니다.

 

 

dematerialize는 기존의 모양으로 되돌려주는 역할을 합니다.

 

[코드]

studentScore
  // 1
  .filter {
    guard $0.error == nil else {
      print($0.error!)
      return false
    }

    return true
  }
  // 2
  .dematerialize()
  .subscribe(onNext: {
    print($0)
  })
  .disposed(by: disposeBag)

/*
--- Example of: materialize and dematerialize ---
80
85
anError
100
*/

dematerialize를 통해 studentScore observable을 원래 모양으로 리턴하고, 점수와 정지 이벤트를 방출할 수 있도록 합니다.

'iOS > RxSwift' 카테고리의 다른 글

[RxSwift] NotificationCenter, Notification  (0) 2022.11.21
[RxSwift] Combining Operators  (0) 2022.11.18
[RxSwift] Filtering Operators  (0) 2022.10.12
[RxSwift] Subjects  (0) 2022.10.12
[RxSwift] Observable  (0) 2022.10.11