RxSwift: Operators (2) - Transforming, Combining Operators

Seoyoung Lee·2023년 11월 15일
1

RxSwift

목록 보기
5/5
post-thumbnail

Transforming Operators

관찰 가능한 데이터를 새로운 시퀀스로 변형하는 operator이다.

ToArray

여러 개의 항목들을 하나의 배열로 변환해주는 operator이다.

let disposeBag = DisposeBag()

Observable.of(1, 2, 3, 4, 5)
    .toArray()
    .subscribe({
        print($0)
    }).disposed(by: disposeBag)

// 실행결과
// success([1, 2, 3, 4, 5])

toArray() 의 반환 타입이 Single<[Element]> 인데, 그래서 subscribe에 onNext 가 없는 것 같다. 나중에 업데이트 된 것 같은데 더 알아봐야 할 듯

Map

시퀀스의 각 항목들에 함수를 적용하는 함수. Swift의 map과 유사하다.

let disposeBag = DisposeBag()

Observable.of(1, 2, 3, 4, 5)
    .map {
        return $0 * 2
    }.subscribe(onNext: {
        print($0)
    }).disposed(by: disposeBag)

/* 실행 결과
2
4
6
8
10
*/

FlatMap

Observable에 의해 방출된 항목을 다시 Observable로 변형하고 방출된 항목을 다시 하나의 Observable로 평면화한다.

let disposeBag = DisposeBag()

struct Student {
    var score: BehaviorRelay<Int>
}

let john = Student(score: BehaviorRelay(value: 75))
let mary = Student(score: BehaviorRelay(value: 95))

let student = PublishSubject<Student>()
    .flatMap { $0.score.asObservable() }
    .subscribe(onNext: {
        print($0)
    }).disposed(by: disposeBag)

student.onNext(john)
john.score.accept(100)
student.onNext(mary)

// 실행결과
// 75
// 100
// 95

john의 score 값을 변경하려면 같이 accept 메소드를 사용해야 한다.

위 예시처럼 내부에 있는 Observable을 변형하고 평면화할 때 유용하다고 한다.

Map은 단순한 값을 반환하지만 flatMap은 Observable을 반환하는 것이 가장 큰 차이인 것 같다. 아직은 둘의 차이점이 완전히 와닿지는 않음 ㅠ-ㅠ

[RxSwift] map vs flatMap

FlatMapLatest

let disposeBag = DisposeBag()

struct Student {
    var score: BehaviorRelay<Int>
}

let john = Student(score: BehaviorRelay(value: 75))
let mary = Student(score: BehaviorRelay(value: 95))

let student = PublishSubject<Student>()
    .flatMapLatest { $0.score.asObservable() }
    .subscribe(onNext: {
        print($0)
    }).disposed(by: disposeBag)

student.onNext(john)
john.score.accept(100)

student.onNext(mary)
john.score.accept(45)

/* 실행 결과
75
100
95
*/

flatMap에서 발생하는 이벤트 중 가장 최신 이벤트만 관찰하고 싶을 때 사용하는 operator이다.

위 예시에서는 마지막으로 mary를 observe 하고 있기 때문에 john의 score 값을 바꿔도 이벤트가 무시되는 것을 볼 수 있다.

Combining Operators

StartWith

Observable의 항목들을 방출하기 전 특정 시퀀스를 먼저 방출한다.

let disposeBag = DisposeBag()

let numbers = Observable.of(2, 3, 4)

let observable = numbers.startWith(1)
observable.subscribe(onNext: {
    print($0)
}).disposed(by: disposeBag)

/* 실행 결과
1
2
3
4
*/

Concat

둘 이상의 Observable을 하나로 합쳐주는 Operator이다.

let disposeBag = DisposeBag()

let first = Observable.of(1, 2, 3)
let second = Observable.of(4, 5, 6)

let observable = Observable.concat([first, second])

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

/* 실행 결과
1
2
3
4
5
6
*/

Merge

여러 개의 Observable들을 하나로 합치는 operator이다. concat operator와 달리 각 값들의 도착 시간을 기준으로 순서가 정해진다.

let disposeBag = DisposeBag()

let left = PublishSubject<Int>()
let right = PublishSubject<Int>()

let source = Observable.of(left.asObservable(), right.asObservable())

let observable = source.merge()
observable.subscribe(onNext: {
    print($0)
}).disposed(by: disposeBag)

left.onNext(5)
left.onNext(3)
right.onNext(2)
right.onNext(1)
left.onNext(99)

/* 실행 결과
5
3
2
1
99
*/

leftright 를 observe 하는 Observable을 만들고, 여기에 merge operator를 적용한다.

CombineLatest

두 Observable의 최신 항목들을 합쳐서 새로운 결과를 만드는 operator이다.

let disposeBag = DisposeBag()

let left = PublishSubject<Int>()
let right = PublishSubject<Int>()

let observable = Observable.combineLatest(left, right, resultSelector: {
    lastLeft, lastRight in
    "\(lastLeft) \(lastRight)"
})

let disposable = observable.subscribe(onNext: { value in
    print(value)
})

left.onNext(45)
right.onNext(1)
left.onNext(30)
right.onNext(1)
right.onNext(2)

/* 실행 결과
45 1
30 1
30 1
30 2
*/

combineLast operator는 resultSelector 라는 파라미터를 가진다!

WithLatestFrom

source Observable을 다른 Observable들과 결합해서 새로운 observable을 만든다. source Observable에서 항목이 방출될 때만 다른 observable들의 최신 값들과 source observable의 값이 합쳐진다.

let disposeBag = DisposeBag()

let button = PublishSubject<Void>()
let textField = PublishSubject<String>()

let observable = button.withLatestFrom(textField)
let disposable = observable.subscribe(onNext: {
    print($0)
})

textField.onNext("Sw")
textField.onNext("Swif")
textField.onNext("Swift")

button.onNext(())
button.onNext(())

/* 실행 결과
Swift
Swift
*/

위 예제에서는 button 이 source observable이자 트리거 역할을 한다.

Rx: combineLatest vs withLatestFrom

Reduce

Observable이 방출하는 항목들마다 함수를 적용하고 최종 값을 마지막으로 방출한다. 기존에 Swift에 있는 reduce 와 유사하다.

let disposeBag = DisposeBag()

let source = Observable.of(1, 2, 3)

// 방법 1
source.reduce(0, accumulator: +)
    .subscribe(onNext: {
        print($0)
    }).disposed(by: disposeBag)

// 방법 2
source.reduce(0, accumulator: {
    summary, newValue in
    return summary + newValue
}).subscribe(onNext: {
    print($0)
}).disposed(by: disposeBag)

/* 실행 결과
6
*/

Scan

reduce와 유사한데 함수가 적용된 결과값을 계산 과정 중에 계속 방출해준다는 차이점이 있다.

이름 그대로 계산 과정을 스캔하는 느낌 👀

let disposeBag = DisposeBag()

let source = Observable.of(1, 2, 3, 5, 6)

source.scan(0, accumulator: +)
    .subscribe(onNext: {
        print($0)
    }).disposed(by: disposeBag)

/* 실행 결과
1
3
6
11
17
*/
profile
나의 내일은 파래 🐳

0개의 댓글