brunch

매거진 ReactiveX

You can make anything
by writing

C.S.Lewis

by Tilltue Aug 06. 2016

RxSwift, Filtering Observable

Debounce, Distinct, Filter, Take, Skip등


* 이 포스트는 RxSwift 4.3.1, swift 4.2 버전을 기준으로 작성되었습니다.

이벤트들을 특정 조건이 맞을때 발생하도록 이벤트를 필터링 하는 메서드들에 대해 알아보자



1. Debounce

http://rxmarbles.com/#debounce

지정한 시간간격 내에 마지막 하나의 이벤트만 전달한다.

아래 그림처럼 이벤트 간격이 설정(debounce)한 시간대 값보다 짧은 타이밍으로 연속된다면 이벤트가 일어나지 않는다.




예제

let timer = Observable<Int>.interval(3, scheduler: MainScheduler.instance)

timer.debounce(1, scheduler: MainScheduler.instance).debug()

         .subscribe().disposed(by: disposeBag)


결과

2018-10-06 22:24:05.526: (timer) -> Event next(0)

2018-10-06 22:24:06.527: (debounceTest()) -> Event next(0)

2018-10-06 22:24:08.526:  (timer) -> Event next(1)

2018-10-06 22:24:09.528:  (debounceTest()) -> Event next(1)

2018-10-06 22:24:11.525:  (timer) -> Event next(2)

2018-10-06 22:24:12.526:  (debounceTest()) -> Event next(2)


시간값을 보면 3초마다 발생한 timer 가 발생하고 debounce 에서 설정한 1초 내에 다른 이벤트가 발생하지 않으므로 1초뒤 debounce된 Observable에 이벤트가 발생한다.


만약 debounce 에 4초를 설정한다면 이벤트는 3초내에 다시 발생하므로 무시되어 

debounce된 Observable에 이벤트가 발생되지 않는다.


- throttle

지정된 시간 내에 발생한 최초및 가장 최신의 이벤트를 발생시킨다.

debounce 와는 동작이 다르다.


예제

let timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)

timer.throttle(3, scheduler: MainScheduler.instance).debug()

         .subscribe().disposed(by: disposeBag)


결과

2018-10-06 22:29:34.234: (timer) -> Event next(0)

2018-10-06 22:29:34.235:  (throttleTest()) -> Event next(0)

2018-10-06 22:29:35.234:  (timer) -> Event next(1)

2018-10-06 22:29:36.234:  (timer) -> Event next(2)

2018-10-06 22:29:37.234:  (timer) -> Event next(3)

2018-10-06 22:29:37.234:  (throttleTest()) -> Event next(3)

2018-10-06 22:29:38.234:  (timer) -> Event next(4)

2018-10-06 22:29:39.234:  (timer) -> Event next(5)


subscribe 된 시점에 이벤트가 발생되고, 이후 3초 간 발생한 이벤트중 가장 최근의 것을 발생시킨다.

유저 탭 이벤트등의 연속된 호출을 간단하게 필터링 할수 있다.


2. Distinct

http://rxmarbles.com/#distinct

이전 이벤트와 비교해서 값이 다를 경우에만 이벤트를 방출한다.

- distinctUntilChanged() 

같은 원소인지 비교해서 다른 원소일때에만 이벤트가 방출된다.


예제

            let test = ["a","a","b","c","c","c"]

            let distinctTest = Observable.from(test).distinctUntilChanged()

            distinctTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)

결과

next(a)

next(b)

next(c)

completed


예제

            struct Fish {

                var name = ""

                var skinColor = ""

            }

            let nimo = Fish(name: "nimo", skinColor: "red")

            let dori = Fish(name: "dori", skinColor: "blue")

            let dori2 = Fish(name: "dori2", skinColor: "blue")

            let test = [nimo,nimo,dori,dori,dori2,nimo]

            let distinctTest = Observable.from(test).distinctUntilChanged { $0.skinColor  }

            distinctTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)


Equatable type 이 아닌 객체를 key 를 지정해서 비교할 수 있는 코드이다.

피부색을 비교하도록 했으므로, dori,dori2 는 같은 이벤트로 판단할 것이다.


결과

next((Fish #1)(name: "nimo", skinColor: "red"))

next((Fish #1)(name: "dori", skinColor: "blue"))

next((Fish #1)(name: "nimo", skinColor: "red"))

completed


예제

            let test = [nimo,nimo,dori,dori,dori2,nimo]

            let distinctTest = Observable.from(test).distinctUntilChanged { (lhs, rhs) -> Bool in

                return lhs.name == rhs.name

            }

            distinctTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)


위의 예제에서 skinColor 가 아닌, name 값으로 비교하도록 변경한 것이다.


결과

next((Fish #1)(name: "nimo", skinColor: "red"))

next((Fish #1)(name: "dori", skinColor: "blue"))

next((Fish #1)(name: "dori2", skinColor: "blue"))

next((Fish #1)(name: "nimo", skinColor: "red"))

completed


3. ElementAt

http://rxmarbles.com/#elementAt

지정한 index 의 이벤트만 발생하도록 하는 메서드이다.


예제

            let test = ["a","a","b","c","c","c"]

            let elementAtTest = Observable.from(test).elementAt(2)

            elementAtTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)

결과

next(b)

completed


4. single ( First )

http://rxmarbles.com/#first 

첫번째 이벤트만 발생시킨다.

RxSwift 에서는 single 메서드로 쓰인다.


예제 생략


5. Filter

http://rxmarbles.com/#filter

조건식에 부합하는 이벤트만 발생시킨다.


예제

            let test = ["rabbit","fox","fish","dog","cat"]

            let filterTest = Observable.from(test).filter{ $0.hasPrefix("f") }

            filterTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)


결과

next(fox)

next(fish)

completed


6. sample

http://rxmarbles.com/#sample

sampler observable 의 이벤트에 따라 본래 observable 의 이벤트가 전달된다.

전달할 이벤트가 없을때는 무시된다.


예제

            let observable = Observable<Int>.interval(0.1, scheduler: MainScheduler.instance)

            observable.sample(Observable<Int>.interval(0.5, scheduler: MainScheduler.instance))

            .subscribe{ event in

                    print(event)

            }.disposed(by: disposeBag)

결과

next(4)

next(9)

next(14)

next(19)

next(24)

...


0.1 초 이벤트가 발생하지만, 0.5초 이벤트 Observable sampler를 통해, 위와 같은 결과를 보인다.


7. skip

http://rxmarbles.com/#skip

n개의 이벤트를 스킵한다.

예제

            let test = ["rabbit","fox","fish","dog","cat"]

            let skipTest = Observable.from(test).skip(3)

            skipTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)

결과

next(dog)

next(cat)

completed


- skipWhile

특정 이벤트 발생전까지 이벤트를 skip 한다.


예제

            let test = ["rabbit","fox","fish","dog","cat"]

            let skipTest = Observable.from(test).skipWhile{ $0 != "fish" }

            skipTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)


결과

next(fish)

next(dog)

next(cat)

completed


skipWhileWithIndex

deprecate 되었다. enumerate().skipWhile().map() 을 사용하면 된다.

skipWhile 과 기본적으로 동일하나 index 를 사용할수 있다.


예제

            let test = ["rabbit","fox","fish","fox","cat"]

            let skipTest = Observable.from(test).skipWhileWithIndex{ (item, index) -> Bool in

                return index < 3 || item != "fox"

            }

            skipTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)

결과

next(fox)

next(cat)

completed


- skipUntil

http://rxmarbles.com/#skipUntil

다른 Observable의 이벤트가 발생하기 전까지를 스킵한다.


예제

            let observable = Observable<Int>.interval(0.1, scheduler: MainScheduler.instance)

            observable.skipUntil(Observable<Int>.interval(0.5, scheduler: MainScheduler.instance))

            .subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)

결과

next(5)

next(6)

next(7)

next(8)

...


8. take

http://rxmarbles.com/#take

n개의 이벤트만 발생한다.


예제

            let test = ["rabbit","fox","fish","dog","cat"]

            let takeTest = Observable.from(test).take(2)

            takeTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)

결과

next(rabbit)

next(fox)

completed


- take with duration

일정 시간동안의 이벤트만 발생한다.


예제

        let observable = Observable<Int>.interval(0.1, scheduler: MainScheduler.instance)

        observable.take(0.3, scheduler: MainScheduler.instance)

            .subscribe{ event in

                print(event)

        }.disposed(by: disposeBag)

결과

next(0)

next(1)


- takeLast

http://rxmarbles.com/#takeLast

완료가 되면 count 만큼 이전의 이벤트를 발생한다.

예제

            let test = ["rabbit","fox","fish","dog","cat"]

            let takeTest = Observable.from(test).takeLast(2)

            takeTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)

결과

next(dog)

next(cat)

completed


- takeUntil

http://rxmarbles.com/#takeUntil

다른 Observable 이벤트가 발생할때까지만 본래 Observable 의 이벤트를 발생한다.

예제

        let observable = Observable<Int>.interval(0.1, scheduler: MainScheduler.instance)

        observable.takeUntil(Observable<Int>.interval(0.5, scheduler: MainScheduler.instance))

            .subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)

결과

next(0)

next(1)

next(2)

next(3)

completed


- takeWhile

조건식에 부합될때까지만 이벤트를 발생한다.

예제

            let test = ["rabbit","fox","fish","dog","cat"]

            let takeWhileTest = Observable.from(test).takeWhile{ $0 != "fish" }

            takeWhileTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)

결과

next(rabbit)

next(fox)

completed


takeWhileWithIndex

deprecate 되었다. enumerate().takeWhile().map() 을 사용하면 된다.

기본적으로 takeWhile 이며 인덱스를 사용할수 있다.


예제

            let test = ["rabbit","fox","fish","fox","cat"]

            let takeWhileTest = Observable.from(test).takeWhileWithIndex{ (item,index) -> Bool in

                return index < 2

            }

            takeWhileTest.subscribe{ event in

                print(event)

            }.disposed(by: disposeBag)


결과

next(rabbit)

next(fox)

completed


9. IgnoreElements

모든 이벤트를 무시한다. 

발생되는 이벤트에는 관심이 없고, 에러나 완료만이 의미가 있을때 사용된다.




이벤트를 필터링 할 수 있는 메서드들을 알아보았다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari