RxSwift 스터디 5주차 Filtering Observables
아래는 필터링 옵저버블
- 약간 달라진 것도 있어서 유의

목차
1. 공식문서 카테고리
2. 그 이외에 것들
- enumerated
- throttle
- single
공식문서 카테고리
1. Debounce
- 어떤 이벤트가 발생하면 일정한 딜레이를 갖는데, 그 사이에 또 다른 이벤트가 들어오면 다시 딜레이가 초기화되고, 딜레이 안에 어떤 값도 들어오지 않으면 그 마지막 이벤트만을 방출한다.

테스트를 위해서 이렇게 작성하였는데, 이렇게 작성할 경우 우리가 클릭을 아무리 많이해도 debounce 값은 변경되지 않는다는 것을 확인할 수 있다.
현재 단계에서 asDriver같은게 이해하는데 어려울 수 있으나, 일단은 로직만 알고 넘어가고 뒤의 포스팅에서 자세하게 설명
@IBOutlet weak var debounceLabel: UILabel!
@IBOutlet weak var debounceBtn: UIButton!
@IBOutlet weak var debounClickLabel: UILabel!
var debounceCount = 0
var debounceClickCount = 0
// viewDidAppear
print(" ===== debounce ===== ")
debounceBtn.rx.tap.asDriver()
.debounce(.seconds(2))
.drive(onNext: { (_) in
self.debounceCount += 1
self.debounceLabel.text = "\(self.debounceCount)"
}).disposed(by: disposeBag)
// class filtering
@IBAction func DebounceBtn(_ sender: Any) {
self.debounceClickCount += 1
self.debounClickLabel.text = "\(self.debounceClickCount)"
print("\(self.debounceClickCount)")
}
2. DistinctUntilChanged
- 연속으로 중복된 아이템을 지워준다.

공식문서에서 distinct의 설명을 보면 연속으로 중복된 것을 지워주는게 아닌 파이썬의 set처럼 중복되는 모든 것이라고 하는데 막상 메소드를 찾아보니, 나타나지 않는다. 아마도 RxSwift에서는 안되고 다른 언어에서 지원해주는 것이 아닐까 싶다.
그래서 DistinctUntilChanged로 알아보았는데, 코드를 한번 보자.
대소문자도 구분이 되며, 커스텀해서 사용할 수 있음.
print(" ===== distinctUntilChanged ===== ")
Observable.of("A","A","B","B","A","C","c")
.distinctUntilChanged()
.subscribe(onNext : {
print($0)
}).disposed(by: disposeBag)
// 결과값
A
B
A
C
c
여기서는 띄어쓰기를 기준으로 분류한 것을 배열로 넣었다가, 배열의 원소 중 중복되는 것이 있다면, 중복처리하는 메소드이다.
// viewDidAppear
print(" ===== distinctUntilChanged(:_) ")
customDistinct()
// extension filtering
func customDistinct() {
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut // 애플 공식 문서에 따르면 숫자의 포맷을 맞춤 en으로 맞춰진다.
/*
ex) 123 : one, hundred, twenty-three
10 : ten
110 : one, hundred, ten -> ten 겹침
20 : twenty
200 : two , hundred
210 : two, hundred, ten -> two, hundred 겹침
310 : three, hundred, ten -> hundred, ten 겹침
*/
Observable<NSNumber>.of(10,110,20,200,210,310)
.distinctUntilChanged {a, b in
//각 숫자를 [String] 으로 쪼개서 넣기
guard let aWords = formatter.string(from: a)?.components(separatedBy: " "),
let bWords = formatter.string(from: b)?.components(separatedBy: " ")
else { return false }
var containsMatch = false
//배열을 돌아가면서 a가 b 에 포함되는지 체크
for aWord in aWords where bWords.contains(aWord) {
containsMatch = true
break
}
return containsMatch
// return true가 반환되면 종료되서 subscribe쪽으로 들어가질 않게 돼.
}.subscribe(onNext: {print($0)})
.disposed(by: disposeBag)
}
// 결과값
10
20
200
3. elementAt
- 인덱스에 맞는 이벤트만 꼭 집어서 방출해준다. , 예를 들면 우리가 특정하게 n번째에 발생한 이벤트만 처리하고 싶을 때
- 버전에 따라 문법이 바뀌었으니 유의

매우 간단함.
let strikes = PublishSubject<String>()
print(" ===== elementAt ===== ")
Observable.of(0,1,2,3,4,5,6,7,8,9)
.elementAt(3)
.subscribe(onNext : {
print($0)
}).disposed(by: disposeBag)
strikes.elementAt(2)
.subscribe(onNext : { event in
print(event)
}, onCompleted: {
print("Complete")
}).disposed(by: disposeBag)
strikes.onNext("A")
strikes.onNext("B")
strikes.onNext("C")
// 결과값
3
C
Complete
4. Filter
- 안의 내용을 만족한 것들만 방출한다.

그냥 값을 필터링 하는거라 뭐가 없음.
print(" ===== Filter ===== ")
Observable.of(1,2,3,4,5,6)
.filter({ (int) -> Bool in
int % 2 == 0
})
.subscribe(onNext : {
print($0)
}).disposed(by: disposeBag)
// 결과값
2
4
6
5. First
- First는 스트림의 첫번째 부분만 내보내준다.

First는 처음꺼만 처리하므로 onNext가 들어갈 경우 에러가 난다.

strike의 경우에는 onNext를 통해 값이 들어왔을때, success되는데, PublishSubject가 아닌 다른 것을 사용한다면 이전 값이 존재하면 그걸 읽고 바로 끝낼 수도 있음.
print(" ===== First ===== ")
Observable.of(1,2,3,4,5,6)
.first()
.subscribe({
print("observable - first success \($0)")
}).disposed(by: disposeBag)
strikes.first().subscribe(onSuccess: {
print("strike - first sucess \($0)")
}, onError: nil).disposed(by: disposeBag)
strikes.first().subscribe(onSuccess: {
print("strike - second sucess \($0)")
}, onError: nil).disposed(by: disposeBag)
strikes.onNext("C")
strikes.onNext("D")
// 결과값
observable - first success success(Optional(1))
strike - first sucess Optional("C")
strike - second sucess Optional("C")
6. ignoreElements
- 방출하진 않지만, 종료 알림은 하겠다. 즉, next는 무시하고 에러나 컴플릿의 스탑 이벤트만 처리하겠다는 의미

간단한 코드로 쉽게 이해 가능.
print(" ===== ignoreElements ===== ")
strikes.ignoreElements()
.subscribe {
print("complete")
} onError: { error in
print("error -> \(error.localizedDescription)")
}
strikes.onNext("1")
strikes.onNext("2")
strikes.onNext("3")
strikes.onCompleted()
// 결과값
complete
7. Last
- Last는 마지막 아이템만 방출한다.
- 이것의 경우에는 last로 직접 구현되는게 아니라, 특정 함수를 구현하는 차단 함수로 구현하며, 이에 따라서 필터링 연산자에서 우리는 takelast(1)을 이용한다

print(" ===== takelast ===== ")
Observable.of(1,2,3,4,5)
.takeLast(1)
.subscribe(onNext : { print($0) })
.disposed(by: disposeBag)
// 결과값
5
8. Sample
- - 특정 기간 사이에 있는 아이템 중 가장 최근의 아이템만을 방출한다.

결과값을 보면 쉽게 이해가 가지? data 서브젝트가 아무리 들어와도 trigger이벤트가 발생할 때, trigger이벤트 사이의 값중에서 가장 나중의 data서브젝트에 있는 값을 방출한다.
let data = PublishSubject<String>() // 선언부
let trigger = PublishSubject<Void>() // 선언부
print(" ===== smaple ===== ")
data.sample(trigger)
.subscribe{ print($0) }
.disposed(by: disposeBag)
trigger.onNext(())
data.onNext("first")
data.onNext("last")
trigger.onNext(())
trigger.onNext(())
data.onCompleted()
trigger.onNext(())
// 결과값
next(last)
completed
9. skip & skipLast & skipWhile &
- skip : 앞에서부터 skip(value) value에 적힌 값의 갯수만큼 스킵한 후에 보여준다.
- skipLast : 앞에서부터 n개를 스킵하는데, 스킵한 값이 다음 밸류가 들어오면 이게 맞게 보여진다.
- skipWhile : 어떤 특정한 조건이 만족하기 전까지 모두 다 스킵
- skipUntil : 특정 이벤트 발생 후 그 시점부터 방출함. 그전까지 모두 스킵




let trigger = PublishSubject<Void>() // 선언부
let subject = PublishSubject<String>() // 선언부
print(" ===== skip ===== ")
Observable.of(1,2,3,4,5,6)
.skip(2)
.subscribe({ print($0) })
.disposed(by: disposeBag)
print(" ===== skipWhile ===== ")
Observable.of(2,2,3,2,4,5,6)
.skipWhile({$0 % 2 == 0})
.subscribe(onNext : { print($0) })
.disposed(by: disposeBag)
print(" ===== skipUntil ===== ")
subject
.skipUntil(trigger)
.subscribe(onNext: { print($0) })
.disposed(by: disposeBag)
subject.onNext("A")
subject.onNext("B")
trigger.onNext(())
subject.onNext("C")
// 결과값
===== skip =====
next(3)
next(4)
next(5)
next(6)
completed
===== skipWhile =====
3
2
4
5
6
===== skipUntil =====
C
10. Take & TakeLast & TakeWhile & TakeUntil
- Take : 앞에서부터 정해진 숫자만큼만 방출
- TakeLast : 뒤에서부터 정해진 숫자만큼만 방출
- TakeWhile : 특정 조건이 만족되기 전까지만 방출
- TakeUntil : 특정 이벤트가 발생하기 전까지만 방출




print(" ===== take ===== ")
Observable.of(1,2,3,4,5,6)
.take(3)
.subscribe(onNext : { print($0) })
.disposed(by: disposeBag)
print(" ===== takelast ===== ")
Observable.of(1,2,3,4,5,6)
.takeLast(3)
.subscribe(onNext : { print($0) })
.disposed(by: disposeBag)
print(" ===== takewhile ===== ")
Observable.of(2,2,3,2,4,5,6)
.takeWhile({ $0 % 2 == 0})
.subscribe(onNext : { print($0) })
.disposed(by: disposeBag)
print(" ===== takeuntil ===== ")
subject
.takeUntil(trigger)
.subscribe(onNext: { print("subscribe value \($0)") })
.disposed(by: disposeBag)
subject.onNext("1")
subject.onNext("2")
trigger.onNext(())
subject.onNext("C")
// 결과값
===== take =====
1
2
3
===== takelast =====
4
5
6
===== takewhile =====
2
2
===== takeuntil =====
1
subscribe value 1
2
subscribe value 2
C
2. 그 외에 것들에 대해서 알아보자.
1. enumerated
- 방출된 요소의 index를 참고하고 싶은 경우가 있을 것입니다. 이럴 때는 enumerated 연산자를 확인할 수 있습니다.
- 기존 Swift의 enumerated 메소드와 유사하게, Observable에서 나오는 각 요소의 index와 값을 포함하는 튜플을 생성하게 됩니다.
- 코드보면 쉽게 이해가 간다! map을 통해 원소를 매핑해주는 작업이 필요
print(" ===== enumerated ===== ")
Observable.of(2, 4, 7, 8, 2, 5, 4, 4, 6, 6)
.enumerated()
.takeWhile({ index, value in
value % 2 == 0 && index < 4
})
.map { $0.element }
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
// 결과값
2
4
// map을 주석처리 하였을 때의 결과값
(index: 0, element: 2)
(index: 1, element: 4)
2. throttle
- 입력 - > 바로 입력 -> 대기
- 주로 버튼 중복 입력 방지에 사용
- Debounce와 비슷하나, Debounce는 마지막 이벤트를 기준으로 딜레이 후 보여진다면, 얘는 클릭 이후 n초간 딜레이를 갖는다.
print(" ===== throttle ===== ")
ThrottleBtn.rx.tap.asDriver()
.throttle(.seconds(3))
.drive(onNext: { (_) in
self.ThrottleCount += 1
self.ThrottleLabel.text = "\(self.ThrottleCount)"
}).disposed(by: disposeBag)
3. single
- siwft sequence의 first와 같으나 아래 보이다 시피 매개변수를 넣을 수 있음.

print(" ===== single ===== ")
Observable.of("📱", "⌚️", "💻", "🖥")
.single()
.subscribe { print($0) }
.disposed(by: disposeBag)
// 결과값
next(📱)
'apple > RxSwift, ReactorKit' 카테고리의 다른 글
| RxSwift 스터디 7주차 Combining Observables (0) | 2021.07.11 |
|---|---|
| RxSwift 스터디 6주차 Transforming Observables (0) | 2021.07.10 |
| RxSwift 스터디 4주차 Subject (0) | 2021.07.10 |
| RxSwift 스터디 3주차 Creating Observables (0) | 2021.07.09 |
| RxSwift 스터디 2주차 Observable (0) | 2021.07.08 |