[RxSwift] Transforming Operators

Posted by Sung Kyungmo on December 11, 2019

변환 연산자


toArray

  • 하나의 요소를 방출하는 옵저버블로 변환 (Single)
  • 더이상 요소를 방출하지 않는 시점에 배열에 담아 전달
1
2
3
4
5
6
7
8
9
10
11
12
13
14
let subject = PublishSubject<Int>()

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

subject.onNext(1)
subject.onNext(2)
subject.onNext(3)
subject.onCompleted()

----- RESULT -----
success([1, 2, 3])

map

  • 원본 옵저버블이 방출하는 요소를 대상으로 함수를 실행하고 결과를 새로운 옵저버블로 리턴
1
2
3
4
5
6
7
8
9
10
11
12
let skills = ["Swift", "SwiftUI", "RxSwift"]

Observable.from(skills)
    .map { $0.count }
    .subscribe{ print($0) }
    .disposed(by: disposeBag)

----- RESULT -----
next(5)
next(7)
next(7)
completed

flatMap

  • 원본 옵져버블이 방출하는 항목을 새로운 옵져버블로 변환
  • 새로운 옵져버블은 항목이 업데이트 될때마다 새로운 항목을 방출
  • 최종적으로 하나의 옵져버블로 합쳐지고 모든항목이 옵져버블을 통해 방출
  • 네트워크 요청을 구현할때 활용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let a = BehaviorSubject(value: 1)
let b = BehaviorSubject(value: 2)

let subject = PublishSubject<BehaviorSubject<Int>>()

subject
    .flatMap{ $0.asObserver() }
    .subscribe{ print($0) }
    .disposed(by: disposeBag)

subject.onNext(a)
subject.onNext(b)

a.onNext(11)
b.onNext(22)

----- RESULT -----
next(1)
next(2)
next(11)
next(22)

flatMapFirst

  • 첫번째로 변환된 옵져버블이 방출하는 항목만 구독자로 전달
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let a = BehaviorSubject(value: 1)
let b = BehaviorSubject(value: 2)

let subject = PublishSubject<BehaviorSubject<Int>>()

subject
   .flatMapFirst { $0.asObservable() }
   .subscribe { print($0) }
   .disposed(by: disposeBag)

subject.onNext(a)
subject.onNext(b)

a.onNext(11)
b.onNext(22)
b.onNext(222)
a.onNext(111)

----- RESULT -----
next(1)
next(11)
next(111)

flatMapLatest

  • 가장 최근의 항목을 방출한 옵저버블의 요소만 방출
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
let a = BehaviorSubject(value: 1)
let b = BehaviorSubject(value: 2)

let subject = PublishSubject<BehaviorSubject<Int>>()

subject
   .flatMapLatest { $0.asObservable() }
   .subscribe { print($0) }
   .disposed(by: disposeBag)

subject.onNext(a)
a.onNext(11)

subject.onNext(b)
b.onNext(22)

a.onNext(11)

----- RESULT -----
next(1)
next(11)
next(2)
next(22)

scan

  • 기본값으로 연산을 시작
  • 원본 옵저버블이 방출하는 항목을 대상으로 변환을 실행한 다음 결과를 방출하는 하나의 옵저버블을 리턴
  • 원본이 방출하는 항목의 수 = 구독자로 전달되는 항목의 수
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1~10의 합
Observable.range(start: 1, count: 10)
    .scan(0, accumulator: +)
    .subscribe{ print($0) }
    .disposed(by: disposeBag)

----- RESULT -----
next(1)
next(3)
next(6)
next(10)
next(15)
next(21)
next(28)
next(36)
next(45)
next(55)
completed

buffer

  • 특정 주기동안 옵져버블이 방출하는 항목을 수집하고 하나의 배열로 리턴
  • 컨트롤드 버퍼링이라고 함
  • 파라미터는 최대시간, 최대갯수(하나만 충족하면 방출)
  • 수집된 배열을 방출하는 옵저버블을 리턴
1
2
3
4
5
6
7
8
9
10
11
12
13
Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .buffer(timeSpan: .seconds(2), count: 3, scheduler: MainScheduler.instance)
    .take(5)
    .subscribe{ print($0) }
    .disposed(by: disposeBag)

----- RESULT -----
next([0])
next([1, 2, 3])
next([4, 5])
next([6, 7])
next([8, 9])
completed

window

  • 최대시간, 최대갯수를 지정해 원본 옵저버블이 방출하는 항목들을 작은 단위의 옵저버블로 분해
  • 옵저버블을 방출하는 옵저버블을 리턴(Inner Observable)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
    .window(timeSpan: .seconds(2), count: 3, scheduler: MainScheduler.instance)
    .take(5)
    .subscribe{
        print($0)
        
        if let observable = $0.element {
            observable.subscribe { print("inner : \($0)") }
        }
}
    .disposed(by: disposeBag)

----- RESULT -----
next(RxSwift.AddRef<Swift.Int>)
inner : next(0)
inner : completed
next(RxSwift.AddRef<Swift.Int>)
inner : next(1)
inner : next(2)
inner : next(3)
inner : completed
next(RxSwift.AddRef<Swift.Int>)
inner : next(4)
inner : next(5)
inner : completed
next(RxSwift.AddRef<Swift.Int>)
inner : next(6)
inner : next(7)
inner : completed
next(RxSwift.AddRef<Swift.Int>)
completed
inner : next(8)
inner : next(9)
inner : completed

groupBy

  • 방출되는 요소를 조건에 따라 그룹핑
  • flatMap, toArray 를 활용해 최종 결과를 하나의 배열로 방출할 수 있음
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let words = ["Apple","Banana","Orange","Book","City","Axe"]

// 문자열의 길이를 기준으로 그룹핑
Observable.from(words)
    .groupBy { $0.count }
    .subscribe(onNext: { (groupedObservable) in
        print("== \(groupedObservable.key)")
        groupedObservable.subscribe{ print(" \($0)") }
    })
    .disposed(by: disposeBag)

----- RESULT -----
== 5
 next(Apple)
== 6
 next(Banana)
 next(Orange)
== 4
 next(Book)
 next(City)
== 3
 next(Axe)
 completed
 completed
 completed
 completed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let words = ["Apple","Banana","Orange","Book","City","Axe"]

// 첫번째 문자로 그룹핑
Observable.from(words)
    .groupBy { $0.first }
    .flatMap{ $0.toArray() }
    .subscribe{ print($0) }
    .disposed(by: disposeBag)

----- RESULT -----
next(["City"])
next(["Orange"])
next(["Banana", "Book"])
next(["Apple", "Axe"])
completed
1
2
3
4
5
6
7
8
9
10
11
// 홀짝으로 그룹핑
Observable.range(start: 1, count: 10)
    .groupBy{ $0.isMultiple(of: 2) }
    .flatMap{ $0.toArray() }
    .subscribe{ print($0) }
    .disposed(by: disposeBag)

----- RESULT -----
next([1, 3, 5, 7, 9])
next([2, 4, 6, 8, 10])
completed


Reference


http://reactivex.io/
https://kxcoding.com/