buffer, flatMap, map, scan, window
* 이 포스트는 RxSwift 4.3.1, swift 4.2 버전을 기준으로 작성되었습니다.
Observable 의 이벤트를 타입을 변경하거나, 새로운 모델로 이벤트를 가공하거나 등, 이벤트를 변형하고자 할때 사용하는 메서드들에 대해 알아보자.
RxSwift 에서 이와 관련된 메서드들은buffer, flatMap, map,scan,window, groupBy 이다.
이벤트를 버퍼에 저장한뒤 묶어서 방출한다.
timeSpan: 버퍼에 저장되는시간간격
count: 버퍼에 저장되는 최대 이벤트 갯수
함수 원형
func buffer(timeSpan: RxTimeInterval,
count: Int,
scheduler: SchedulerType)
- 모델카지노<[E]
예제
let bufferTest = Observable<Int.interval(1.0, scheduler: MainScheduler.instance)
bufferTest.buffer(timeSpan: 3, count: 3, scheduler: MainScheduler.instance)
.subscribe{ event in
print(event)
}.disposed(by: disposeBag)
결과
next([0, 1])
next([2, 3, 4])
next([5, 6, 7])
최초 3초 동안 발생한 이벤트는 2개 이므로 2개가 묶여져서 observer 에게 전달되고, 이후에 3개씩 이벤트가 묶여져서 전달된다.
시간값과 count 를 조절하면서 테스트 해보면 어떻게 사용되는지 알 수 있다.
개인적인 의견 : 동적으로 이벤트를 생성하는 코드가 있고, 성능이나, 기타 이유에 의해 어느정도 이벤트들을 묶어서 처리해야 하는 경우 사용하면 좋을 것 같다.
(flatMapWithIndex,flatMapFirst,flatMapLatest)
원본모델카지노의 이벤트를 받아새로운 모델카지노 로 변형한다.
예제
let timer = Observable<Int.interval(1.0, scheduler: MainScheduler.instance)
letflatMapTest = timer.flatMap { (num) - Observable<String in
return Observable<String.create{ observer in
observer.on(.next("AA\(num)"))
observer.on(.next("AA\(num)"))
return Disposables.create {
print("dispose")
}
}
}
flatMapTest.subscribe{ event in
print(event)
}.disposed(by: disposeBag)
결과
next(AA0)
next(BB0)
next(AA1)
next(BB1)
next(AA2)
next(BB2)
next(AA3)
next(BB3)
deprecate 되었다. enumerate().flatMap 을 사용하면 된다.
이벤트와 인덱스를 같이 받을수 있다.
예제
let timer = Observable<Int.interval(1.0, scheduler: MainScheduler.instance)
let flatMapTest = timer.flatMapWithIndex{ (num, index) - Observable<String in
return Observable<String.create{ observer in
observer.on(.next("AA\(index,num)"))
observer.on(.next("BB\(index,num)"))
return Disposables.create {
print("dispose")
}
}
}
flatMapTest.subscribe{ event in
print(event)
}.addDisposableTo(disposeBag)
결과
next(AA(0, 0))
next(BB(0, 0))
next(AA(1, 1))
next(BB(1, 1))
next(AA(2, 2))
next(BB(2, 2))
원본 모델카지노의 이벤트를 받아새로운 모델카지노 로 변형하지만, 이 Observable 이 완료되기 전에 이전 Observable 의 다른 이벤트들은 무시하게 된다.
예제
let timer = Observable<Int.interval(1.0, scheduler: MainScheduler.instance)
let flatMapTest =timer.flatMapFirst{ (num) - Observable<String in
return Observable<String.create{ observer in
observer.on(.next("AA\(num)"))
observer.on(.next("BB\(num)"))
observer.onCompleted()
return Disposables.create {
print("dispose")
}
}.delaySubscription(3, scheduler: MainScheduler.instance)
}
flatMapTest.subscribe{ event in
print(event)
}.disposed(by: disposeBag)
flatmap 으로 이벤트를 변환한 Observable 은 subscription 에 3초의 딜레이를 줬다.
3초간 발생한 원본 모델카지노 의 이벤트들이 무시된것을 확인 할 수 있다.
결과
next(AA0)
next(BB0)
dispose
next(AA4)
next(BB4)
dispose
원본 모델카지노의 이벤트를 받아새로운 모델카지노 로 변형하지만, 다음 원본 이벤트가 발생하면dispose 되고 다시 변형된 Observable 을 생성한다. subscription에 dispose 가 전달 되지는 않는다.
let timer = Observable<Int.interval(1.0, scheduler: MainScheduler.instance)
let flatMapTest = timer.flatMapLatest{ (num) - Observable<Int in
return Observable<Int.interval(0.3, scheduler: MainScheduler.instance)
}
flatMapTest.subscribe{ event in
print(event)
}.disposed(by: disposeBag)
1초씩 발생하는 원본 Observable 의 다음 이벤트가 발생하기 전까지 flatMap 으로 변환된 0.3초당 한번으로 변형된 이벤트가 전달되고, 이후 이벤트가 전달되었을때 다시 0.3초당 한번 이벤트를 발생하는 옵저버블이 수행된다.
결과
next(0)
next(1)
next(2)
---- 1초
next(0)
next(1)
next(2)
---- 2초
next(0)
(mapWithIndex)
이벤트를 다른 이벤트로 변환한다.
예제
let timer = Observable<Int.interval(1.0, scheduler: MainScheduler.instance)
let mapTest = timer.map{ "map test\($0)" }
mapTest.subscribe{ event in
print(event)
}.disposed(by: disposeBag)
결과
next(map test0)
next(map test1)
next(map test2)
deprecate 되었다. enumerate().map 을 사용하면 된다.
map 과 동일하지만 아이템과 인덱스를 같이 받을수 있다.
예제
let timer = Observable<Int.interval(1.0, scheduler: MainScheduler.instance)
let mapTest = timer.mapWithIndex{ (num, index) - String in
return "map test\(num) index:\(index)"
}
mapTest.subscribe{ event in
print(event)
}.disposed(by: disposeBag)
결과
next(map test0 index:0)
next(map test1 index:1)
next(map test2 index:2)
next(map test3 index:3)
scan 은 값을 축적해서(혹은 변경 없이 저장) 가지고 있을 수 있으며, 이 값을 통해 이벤트를 변형할 수 있는 메서드이다.
함수 원형
func scan<A(into seed: A,
accumulator: @escaping (inout A, E) throws - ())
- 모델카지노<A
변형하는 이벤트의 타입은 원본 이벤트 타입과 같아야 함을 알수있다.
예제
let timer = Observable<Int.interval(1.0, scheduler: MainScheduler.instance)
let scanTest = timer.scan(10) { (accumulator, num) - Int in
return accumulator + num
}
scanTest.subscribe{ event in
print(event)
}.disposed(by: disposeBag)
초기값 10을 가지도록 했고이벤트가 발생하면 그값을 더해 축적시키도록 변형했다.
결과
next(10)
next(11)
next(13)
next(16)
next(20)
buffer 와 유사하지만모여진 이벤트로 새로운 모델카지노 을 생성한다는 것이 다르다.
함수 원형
func window(timeSpan: RxTimeInterval,
count: Int,
scheduler: SchedulerType)
- 모델카지노<모델카지노<E
예제
let rangeObservable = Observable<Int.range(start: 0, count: 10)
let windowTest = rangeObservable.window(timeSpan: 1000, count: 3, scheduler: MainScheduler.instance)
windowTest.subscribe(onNext: { [unowned self] 모델카지노in
모델카지노.subscribe{ event in
print(event)
}.disposed(by: disposeBag)
}).disposed(by: disposeBag)
원본 이벤트 를 최대 3개씩 묶어 새로운 모델카지노을 이벤트로 전달하도록 변형되었다.
결과
next(0)
next(1)
next(2)
completed
next(3)
next(4)
next(5)
completed
next(6)
next(7)
next(8)
completed
next(9)
completed
기본값을 가지고, emit 된 값들을 연산해서 하나의 결과값 이벤트를 발생하는 Observable 로 변형한다.
두가지 함수 원형
func reduce<A, R(_ seed:A,
accumulator: @escaping (A, E) throws - A,
mapResult: @escaping (A) throws - R)
- 모델카지노<R
func reduce<A(_ seed: A,
accumulator: @escaping (A, E) throws - A)
- 모델카지노<A
seed 값을 기본으로 연산자를 받아 결과값을 만들어 낸다.
예제
let rangeObservable = Observable<Int.range(start: 0, count: 10)
let reduceTest = range모델카지노.reduce(0, accumulator: +)
reduceTest.subscribe{ event in
print(event)
}.disposed(by: disposeBag)
결과
next(45)
completed
이벤트들을 분류해서 key 값을통해 다른 Observable 로 변형할수 있다.
keySelector 에서 각 요소들에서 키값을 추출하는 함수를 전달한다.
groupby 는 이 키 값과 본래 값을 같이 가진 Observable 로 변형시켜준다.
함수 원형
func groupBy<K: Hashable(keySelector: @escaping (E) throws - K)
- 모델카지노<Grouped모델카지노<K,E
예제
let timer = Observable<Int.interval(1, scheduler: MainScheduler.instance)
timer.groupBy{ $0 % 2 == 0 }.flatMap{ grouped - Observable<String in
if grouped.key{
return grouped.map{ "even \($0)" }
}else {
return grouped.map{ "odd \($0)" }
}
}.debug().subscribe().disposed(by: disposeBag)
결과
(groupByTest()) - subscribed
(groupByTest()) - Event next(even 0)
(groupByTest()) - Event next(odd 1)
(groupByTest()) - Event next(even 2)
(groupByTest()) - Event next(odd 3)
홀수와 짝수를 구분하도록 한 예제이다.
각 이벤트를 구별할수 있고 그 이벤트들로 별도의 Observable 을 만들어야 할때 유리하다.
Observable 의 변형을 통해, 발생하는 이벤트들을 상황에 따라 유연하게 처리가 가능하다.