- 오늘의 주제는 filtering operator입니다.
- 다들 Swift에서
filter(_:)를 사용해보신 적이 있으시죠? - 말그대로 Subject에 필터를 껴주는 작업입니다.
- 우리가 원하는 event 값만 쏙쏙 골라서 emit 해줄 수 있습니다. (개꿀~)
public func example(of description: String,
action: () -> Void) {
print("\n— Example of:", description, "—")
action()
}- 지난 10월에 버전 6.0.0이 릴리즈 되었습니다.
- 오늘 공부하는 부분 중에 변경된 부분이 있습니다. 6.0.0 버전이 아니신 분들은 제 코드에서 오류가 뜰 수 있습니다.
- 아래의 릴리즈 노트를 참고해주세요!
- 릴리즈 노트 보러가기
ignoreElements는.next이벤트는 무시합니다.completed나.error같은 정지 이벤트는 허용합니다.
example(of: "ignoreElements") {
// 1
let strikes = PublishSubject<String>()
let disposeBag = DisposeBag()
// 2
strikes
.ignoreElements()
.subscribe(
onNext: { event in
print(event)
},
onCompleted: {
print("Complete")
}
)
.disposed(by: disposeBag)
strikes.onNext("1")
strikes.onNext("2")
strikes.onNext("3")
strikes.onCompleted()
}출력을 해보셨나요?
— Example of: ignoreElements —
Complete이렇게 1,2,3 모두 무시되고 complete만 출력되는 것을 확인할 수 있습니다.
- 우리는 방출된 특정 n번째의 이벤트만 처리하고 싶을 때가 있을 수 있습니다.
- 이럴 때 사용하는 것이
elementAt()입니다 - 문법이 바뀌었습니다.
elementAt()->element(at: )이렇게 써줘야 합니다. - 당연하게 우리에게 index 0은 1번째 방출되는 이벤트 ^-^...
example(of: "elementAt") {
// 1
let strikes = PublishSubject<String>()
let disposeBag = DisposeBag()
// 2
strikes
.element(at: 2)
.subscribe(
onNext: { event in
print(event)
},
onCompleted: {
print("Complete")
}
)
.disposed(by: disposeBag)
strikes.onNext("a")
strikes.onNext("b")
strikes.onNext("c")
}우선 어떻게 나올지 생각해보고, 출력을 해봅시다
— Example of: elementAt —
c
Complete생각했던대로 나오셨나요?
ignoreElements와element(at: )은 observable의 요소들을 필터링하여 방출합니다.filter는 필터링 요구사항이 한 가지 이상일 때 사용할 수 있습니다.
example(of: "filter") {
let disposeBag = DisposeBag()
// 1
Observable.of(1,2,3,4,5,6)
// 2
.filter({ (int) -> Bool in
int % 2 == 0
})
// 3
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
}
- Int 값을 받는 observable 생성
- filter를 생성하여 true 값을 반환하는 요소만 내보낸다!
- subscribe를 사용하여 방출되는 값을 확인한다
— Example of: filter —
2
4
6
End- 몇개의 요소를 skip 하고 싶을 때가 있을 수 있다고 하네요 .. ( 왜? 언제? 모름 암튼 )
skip연산자는 첫 번째 요소부터 n개의 요소를 skip하게 해줍니다
example(of: "skip") {
let disposeBag = DisposeBag()
// 1
Observable.of("A", "B", "C", "D", "E", "F")
// 2
.skip(3)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
}
앞의 3개 A,B,C 를 제외하고 D,E,F 만 출력되는 것을 확인하실 수 있습니다
— Example of: skip —
D
E
FskipwWhile은 skip할 로직을 구성하고 해당 로직이false되었을 때 방출합니다.- 문법이 바뀌었습니다.
skipWhile(_:)->skip(while:)이렇게 써줘야 합니다.
example(of: "skipWhile") {
let disposeBag = DisposeBag()
// 1
Observable.of(2, 2, 3, 4, 4)
// 2
.skip(while: {$0 % 2 == 0})
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
}- 홀수인 요소가 나올 때까지 skip합니다. 홀수인 요소가 나오면 그 후에는 계속 방출합니다.
- 보험금 청구 앱을 개발한다고 가정해보면, 공제액이 충족될 때까지 보험금 지급을 거부하기 위해
skipWhile을 사용할 수 있습니다.
— Example of: skipWhile —
3
4
4skipUnitl은 다른 observable이 시동할 때까지 현재 observable에서 방출하는 이벤트를 skip 합니다skipUnitl은 다른 observable이.next이벤트를 방출하기 전까지는 기존 observable에서 방출하는 이벤트들을 무시하는 것 !!
example(of: "skipUntil") {
let disposeBag = DisposeBag()
// 1
let subject = PublishSubject<String>()
let trigger = PublishSubject<String>()
// 2
subject
.skipUntil(trigger)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
// 3
subject.onNext("A")
subject.onNext("B")
// 4
trigger.onNext("X")
// 5
subject.onNext("C")
}코드를 실행해봅시다 !
- 주석을 따라 확인해보면,
- a)
subject와trigger라는 PublishSubject를 만든다. - b)
subject를 구독하는데 그 전에.skipUnitl을 통해trigger를 추가한다. - c)
subject에.onNext()로A,B추가한다. - d)
trigger에.onNext()로X를 추가한다. - e)
subject에 새로운 이벤트C를 추가한다. 그제서야C가 방출되는 것을 콘솔에서 확인할 수 있다. 왜냐하면 그 전까지는.skipUnitl이 막고 있었기 때문이다.
— Example of: skipUntil —
C- Taking은 skipping의 반대 개념입니다.
- RxSwift에서 어떤 요소를 취하고 싶을 때 사용할 수 있는 연산자는
take
- 그림을 보면
take()를 통해, 처음 2개의 값을 취한 것을 알 수 있습니다~~~
```swift
example(of: "take") {
let disposeBag = DisposeBag()
// 1
Observable.of(1,2,3,4,5,6)
// 2
.take(3)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
}
```
— Example of: take —
1
2
3느낌이 오시죠 ?
takeWhile은skipWhile처럼 작동합니다.- 그림과 같이
takeWhile구문 내에 설정한 로직에서true에 해당하는 값을 방출하게 됩니다. (skip과 반대) - 문법이 바뀌었습니다.
takeWhile(_:)->take(while:)이렇게 써줘야 합니다.
- 방출된 요소의 index를 참고하고 싶은 경우가 있을 것입니다. 이럴 때는
enumerated연산자를 확인할 수 있습니다! - 기존 Swift의
enumerated메소드와 유사하게, Observable에서 나오는 각 요소의 index와 값을 포함하는 튜플을 생성하게 됩니다.
example(of: "takeWhile") {
let disposeBag = DisposeBag()
// 1
Observable.of(2, 4, 7, 8, 2, 5, 4, 4, 6, 6)
// 2
.enumerated()
// 3
.take(while: { index, value in
// 4
value % 2 == 0 && index < 3
})
// 4
.map { $0.element }
// 5
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
}— Example of: takeWhile —
2
4- 2, 4 까지는 설정한 로직이 true이지만 7은 false 입니다.
- 그 이후의 값들은 방출하지 않습니다.
skipUntil이 있으니takeUntil도 있겠죠 ?- 문법이 바뀌었습니다.
takeUntil(_:)->take(until:)이렇게 써줘야 합니다. - 그림과 같이, trigger가 되는 Observable이 구독되기 전까지의 이벤트값만 받는 것입니다. (skip과 반대라고 생각하면 됨)
example(of: "takeUntil") {
let disposeBag = DisposeBag()
// 1
let subject = PublishSubject<String>()
let trigger = PublishSubject<String>()
// 2
subject
.take(until:trigger)
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
// 3
subject.onNext("1")
subject.onNext("2")
trigger.onNext("X")
subject.onNext("3")
}skip과 유사하니 빠르게 지나가겠습니다 - 슝
— Example of: takeUntil —
1
2-
이 책 마지막에서 배울RxCocoa 라이브러리의 API를 사용하면 dispose bag에 dispose를 추가하는 방식 대신takeUntil을 통해 구독을 dispose 할 수 있다. 아래의 코드를 살펴보자.someObservable .take(until:self.rx.deallocated) .subscribe(onNext: { print($0) })
-
이전의 코드에서는
takeUntil의trigger로 인해서subject의 값을 취하는 것을 멈췄었다. -
여기서는 그 trigger 역할을
self의 할당해제가 맡게 된다. 보통self는 아마 뷰컨트롤러나 뷰모델이 될것이다.
- 여기서 배울 것은 중복해서 이어지는 값을 막아주는 연산자입니다.
- 그림에서처럼
distinctUntilChanged는 연달아 같은 값이 이어질 때 중복된 값을 막아주는 역할을 합니다. 2는 연달아 두 번 반복되었으므로 뒤에 나온2가 배출되지 않음!1은 중복이긴 하지만 연달아 반복된 것이 아니므로 그대로 배출됩니다.
example(of: "distincUntilChanged") {
let disposeBag = DisposeBag()
// 1
Observable.of("A", "A", "B", "B", "A")
//2
.distinctUntilChanged()
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
}— Example of: distinctUntilChanged —
A
B
A너무 쉽죠? ㅎ-ㅎ
distinctUntilChanged는 기본적으로 구현된 로직에 따라 같음을 확인합니다. 그러나 커스텀한 비교로직을 구현하고 싶다면distinctUntilChanged(_:)를 사용할 수 있습니다.- 개인적으로 이게 좀 멋드러진다는 생각이 드네요 ㅎ-ㅎ ( 역시 커스텀이 멋지다 )
example(of: "distinctUntilChanged(_:)") {
let disposeBag = DisposeBag()
// 1
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
// 이 부분은 제가 아래 코드를 이해하기 위해서 실험해본 부분입니다... rx와는 상관 없음
let exampleNum :NSNumber = 110
print("example : \(formatter.string(from: exampleNum)!.components(separatedBy: " "))")
// 2
Observable<NSNumber>.of(10, 110, 20, 200, 210, 310)
// 3
.distinctUntilChanged { a, b in
// 4
guard let aWords = formatter.string(from: a)?.components(separatedBy: " "),
let bWords = formatter.string(from: b)?.components(separatedBy: " ")
else {
return false
}
var containsMatch = false
// 5
for aWord in aWords {
for bWord in bWords {
if aWord == bWord {
containsMatch = true
break
}
}
}
return containsMatch
}
// 4
.subscribe(onNext: {
print($0)
})
.disposed(by: disposeBag)
}— Example of: distinctUntilChanged(_:) —
10
20
200why ?!?!
10 : ten
110 : one, hundred, ten -> ten 겹침
20 : twenty
200 : two , hundred
210 : two, hundred, ten -> two, hundred 겹침
310 : three, hundred, ten -> hundred, ten 겹침- 10 : ten
- 110 : one, hundred, ten -> ten 겹침
- 20 : twenty
- 200 : two , hundred
- 210 : two, hundred, ten -> two, hundred 겹침
- 310 : three, hundred, ten -> hundred, ten 겹침
- 주석을 따라 확인해보자.
-
- 각각의 번호를 배출해내는
NumberFormatter()를 만들어낸다.
- 각각의 번호를 배출해내는
-
NSNumbersObservable을 만든다. 이렇게 하면formatter를 사용할 때 Int를 변환할 필요가 없다.
-
distinctUntilChanged(_:)는 각각의 seuquence 쌍을 받는 클로저다.
-
guard문을 통해 값들의 구성요소를 빈 칸 구분하여 조건부로 바인딩하고 그렇지 않으면false를 반환한다.
-
- 중첩
for-in반복문을 통해서 각 쌍의 단어를 반복하고, 검사결과를 반환하여, 두 요소가 동일한 단어를 포함하는지 확인한다.
- 중첩
-
- 구독하고 출력한다.
- 결과는, 다른 요소를 포함하는 요소는 제외된 결과만 출력된다.
10 20 200 - a, b, c를 비교해가면서 만약 b가 a와 중첩되는 부분이 있어 prevent 되면, 다음엔 b와 c를 비교하는 것이 아니라 a와 b를 비교하게 됩니다. << 이해가 안감 토론 요망
-
끝!










