ios dev kangwook.

6. Transforming Operators 본문

RxSwift

6. Transforming Operators

kangwook 2022. 12. 22. 02:20

Transforming elements

  • Observable 은 아이템을 개별적으로 방출하지만 Observable을 UITableView 또는 UICollectionView에 바인딩 할 때와 같이 컬렉션 작업에 쓰려고 할 수도 있다.
  • Observable 의 개별 아이템을 모든 아이템의 배열로 변환하는 편리한 방법은 toArray 를 사용하는 것
  • toArray 는 Observable 시퀀스를 해당 아이템 배열로 변환하고 해당 배열을 포함하는 .next 이벤트를 구독자에게 내보낸다.

example(of: "toArray") {
    let disposeBag = DisposeBag()
 
    Observable.of("A", "B", "C")
        .toArray()
        .subscribe {
            print($0)
        }
        .disposed(by: disposeBag)
}

 

  • RxSwift의 map 연산자는 Observable에서 동작한다는 점을 제외하면 Swift의 표준 map과 동일하게 동작

example(of: "map") {
    let disposeBag = DisposeBag()
 
    let formatter = NumberFormatter()
    formatter.numberStyle = .spellOut
 
    Observable<Int>.of(123, 4, 56)
        .map {
            formatter.string(for: NSNumber(value: $0)) ?? ""
        }
        .subscribe {
            print($0)
        }
        .disposed(by: disposeBag)
}

 

  • 필터링 연산자와 함께 enumerated() 와 map을 사용할 수 있다.
example(of: "enumerated and map") {
    let disposeBag = DisposeBag()
 
    Observable.of(1, 2, 3, 4, 5, 6)
        .enumerated()
        .map { index, integer in
            index > 2 ? integer * 2 : integer
        }
        .subscribe {
            print($0)
        }
        .disposed(by: disposeBag)
}


Transforming inner observables

  • 진행될 예제를 위해 다음 코드를 추가
struct Student {
    let score: BehaviorSubject<Int>
}
  • flatMap은 Observable 시퀀스의 각 아이템을 Observable 시퀀스에 투영하고 결과로 생성되는 Observable 시퀀스를 하나의 Observable 시퀀스로 병합
  • flatMap은 Observable 값을 투영하고 변환한 다음 Observable 대상으로 평평하게 만든다.

example(of: "flatMap") {
    let disposeBag = DisposeBag()
 
    let laura = Student(score: BehaviorSubject(value: 80))          // Student 타입의 laura 인스턴스 생성
    let charlotte = Student(score: BehaviorSubject(value: 90))      // Student 타입의 charlotte 인스턴스 생성
 
    let student = PublishSubject<Student>()                           // Student 타입의 PublishSubject 생성
 
    student
        .flatMap {                                                  // flatMap을 사용해서 Student 내부의 score에 접근해서 투영
            $0.score
        }
        .subscribe {
            print($0)
        }
        .disposed(by: disposeBag)
}

 

  • 다음 코드 추가
student.onNext(laura)

laura.score.onNext(85)

student.onNext(charlotte)

laura.score.onNext(95)

 

 

  • flatMap은 각 Observable에서 변경 사항을 계속 투영한다.
  • flatMapLatest는 map 과 switchLatest 의 조합
  • switchLatest 는 가장 최근의 Observable에서 값을 생성하고 이전 Observable에서 구독을 취소
  • 따라서 flatMapLatest는 Observable 시퀀스의 각 아이템을 Observable 시퀀스의 새로운 시퀀스로 투영한 다음 Observable 시퀀스를 Observable 시퀀스로 변환하여 가장 최근의 Observable 시퀀스에서만 값을 생성

O1이 구독하고 있어 1을 수신하고 O2가 구독을 하면 O1에 대한 구독을 취소하고 O2를 구독하므로 값 3이 수신되지 않는다.

  • flatMapLatest 는 flatMap 처럼 동작하여 Observable 아이템에 접근하여 Observable 속성에 접근하고 각 아이템에 대한 새 시퀀스를 투영
  • flatMapLatest 가 다른 점은 자동으로 최신 Observable 로 전환하고 이전 항목에서 구독을 취소
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 {
            print($0)
        }
        .disposed(by: disposeBag)
         
    student.onNext(laura)
    laura.score.onNext(85)
    student.onNext(charlotte)
     
    laura.score.onNext(95)
    charlotte.score.onNext(100)
}

  • flatMapLatest 는 가장 일반적으로 네트워크 작업과 함께 사용
  • 예를 들어, 자동 완성 검색을 구현한다고 가정하면 사용자가 문자를 입력할 때 새 검색을 실행하고 이전 검색 결과를 무시할 수 있다.

Observing Events

  • Observable을 해당 이벤트의 Observable로 변환하려는 경우가 있을 수 있다.
  • 예를 들어, Observable 속성이 있는 Observable 항목을 제어할 수 없고 외부 시퀀스가 종료되지 않도록 오류 이벤트를 처리하려는 경우
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)
 
    let studentScore = student.flatMapLatest { $0.score }
 
    studentScore
        .subscribe {
            print($0)
        }
        .disposed(by: disposeBag)
 
    laura.score.onNext(85)
    laura.score.onError(MyError.anError)
    laura.score.onNext(90)
 
    student.onNext(charlotte)
}

 

 

  • 여기서 studentScore 를 다음과 같이 바꾸면
let studentScore = student.flatMapLatest { $0.score.materialize() }

에러 뒤의 .next 이벤트도 출력이 된다.

  • 오류로 인해 studentScore는 종료되지만 외부 학생은 관찰되지 않으므로 새 학생으로 전환하면 해당 점수가 성공적으로 수신되고 출력된다.

 

  • 이제는 아이템이 아닌 이벤트를 다루고 있다.
  • 구체화된 Observable 항목을 원래의 형태로 변환
studentScore
    .filter {
        guard $0.error == nil else {
            print($0.error!)
            return false
        }
 
        return true
    }
    .dematerialize()
    .subscribe {
        print($0)
    }
    .disposed(by: disposeBag)

'RxSwift' 카테고리의 다른 글

8. Time-Based Operators  (0) 2022.12.27
7. Combining Operators  (1) 2022.12.23
5. Filtering Operators  (0) 2022.12.19
4. Observables & Subjects in Practice  (1) 2022.12.13
3. RxSwift Subjects  (0) 2022.12.12
Comments