Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
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
Archives
Today
Total
관리 메뉴

teunteun2

[RxSwift] Filtering Operator - .skip & .distinctUntilChanged (textfield 입력값이 변경될 때에만 이벤트가 발생하도록 하는 방법) 본문

iOS

[RxSwift] Filtering Operator - .skip & .distinctUntilChanged (textfield 입력값이 변경될 때에만 이벤트가 발생하도록 하는 방법)

teunteun2 2022. 4. 17. 21:15

순서

  • Operator란?
  • Filtering Operator
  • .skip
  • .distinctUntilChanged

 

    Operator란?    

Operator, 거의 모든 RxSwift의 연산자는

Observable 에서 동작하고, Observable를 리턴하는데요,

 

따라서 Observable을 생성할 때에도,

Observable 내에서 필요한 항목만 필터링할 때에도,

여러개의 Observable를 합칠 때에도

Observable를 가지고 이리저리 굴리기 위해선 Operator가 꼭 필요합니다.

 

Operator의 큰 특징은 바로

Chaining 이죠 !

 

Chaining이란 연산자를 연이어 실행시켜

체인 형태로 만드는 것을 의미합니다.

예시를 한 번 볼까요 ?

 

 

- TextField로 입력받은 핸드폰번호가 유효한 번호인지 검사하는 코드 -

    struct Input {
        let phoneNum: ControlProperty<String?>
    }

    struct Output {
        let numValidStatus: Observable<Bool>
    }
    
    var disposeBag = DisposeBag()

    func transform(input: Input) -> Output {

        let result = input.phoneNum
            .orEmpty
            .distinctUntilChanged()
            .map { num -> Bool in
                let phoneRegEx = "^01[0-1,6]-[0-9]{3,4}-[0-9]{4}$"
                let pred = NSPredicate(format: "SELF MATCHES %@", phoneRegEx)
                return pred.evaluate(with: num)
            }
            .share(replay: 1, scope: .whileConnected)

        return Output(numValidStatus: result)
    }

 

result 부분을 보면

하나의 연산자가 쓰인 것이 아니라

.orEmpty , .distinctUntilChanged, .map, .share

네가지의 연산자가 연이어 체인처럼

연결된 것을 볼 수 있습니다 !

 

    Filtering Operator    

 

Filtering Operator은

emit된 Obsevable 항목 중

원하는 항목을 필터링 할 수 있는 연산자 입니다.

 

종류는 아래와 같이 다양하게 있기 때문에

하고자 하는 필터링에 따라 선택해서 사용하면 됩니다.

 

  • Debounce — Observable의 시간 흐름이 지속되는 상태에서 다른 항목들은 배출하지 않고 특정 시간 마다 그 시점에 존재하는 항목 하나를 Observable로부터 배출한다
  • Distinct — Observable이 배출하는 항목들 중 중복을 제거한 항목들을 배출한다
  • ElementAt — Obserable에서 n번째 항목만 배출한다
  • Filter — 테스트 조건을 만족하는 항목들만 배출한다
  • First — 맨 첫 번째 항목 또는 조건을 만족하는 첫 번째 항목만 배출한다
  • IgnoreElements — 항목들을 배출하지는 않고 종료 알림은 보낸다
  • Last — Observable의 마지막 항목만 배출한다
  • Sample — 특정 시간 간격으로 최근에 Observable이 배출한 항목들을 배출한다
  • Skip — Observable이 배출한 처음 n개의 항목들을 숨긴다
  • SkipLast — Observable이 배출한 마지막 n개의 항목들을 숨긴다
  • Take — Observable이 배츨한 처음 n개의 항목들만 배출한다
  • TakeLast — Observable이 배출한 마지막 n개의 항목들만 배출한다

<출처 : https://reactivex.io/documentation/ko/operators.html>

 

 

이 중 오늘은 제가 스터디에서 서칭을 맡은

.skip 과 .distinctUntilChanged 에 대해

알아보도록 해요 ㅋㅅㅋ

 

    .skip    

ReactiveX 공식문서를 보면

"Observable이 배출한 처음 n개의 항목들을 숨긴다"

라고 써져있어요

 

만약 발생한 이벤트의 초반 정보가 필요 없다면 

.skip으로 스킵해준 다음

다음 연산자가 이어받을 수 있겠죠?

 

그림만 봐도 쉽게 이해할 수 있는..

skip(2) 2개를 스킵한다는 의미로 보이네요

 

예시 코드로 다시 한 번 볼까용

 

Observable.of("Apple", "Banana", "Grape", "Watermelon")
	.skip(2)
    .subscribe(onNext: {
    	print($0)
    })
    .disposed(by: disposeBag)
 
/*
print 해보면

Grape
Watermelon
*/

 

    .distinctUntilChanged    

distinctUntilChanged 는

공식문서 연산자 결정 트리에 따르면

"Observable의 특정 항목만 재배출 해야 하는데

만약 중복된 항목이 바로 연이어 배출될 때"

사용한다고 정의되어 있어요

 

무슨말이냐면

이전 이벤트 값과 변경된 현재 이벤트 값이

다를 때에만 리턴한다는 뜻입니다 !

 

 

그렇다면 요 연산자는 주로 언제 많이 쓰일까요?

 


 

TextField 예제로 이해해봅시다

아래의 인스타그램 로그인뷰 아이디 TextField에

키보드를 올려 Asdf를 입력하고 키보드를 내렸다고

가정해볼까요 ?

 

mainView.userIdTextField.rx.text
            .subscribe { text in
                print(text)
            }
            .disposed(by: disposeBag)
            
/*
next(Optional(""))
next(Optional("A"))
next(Optional("As"))
next(Optional("Asd"))
next(Optional("Asdf"))
next(Optional("Asdf"))
*/

 

textfield text를 구독해주어

무언가 이벤트가 발생할 때마다 print 되도록 했더니

 

next(Optional(""))

next(Optional("A"))

next(Optional("As"))

next(Optional("Asd"))

next(Optional("Asdf"))

next(Optional("Asdf"))

 

이렇게 출력됩니다.

빈값은 왜 출력되는 것이고

Asdf는 왜 두번 출력이 되는걸까요 ?

 

rx.text 를 타고들어가보면

다음과 같이 text와 value를 볼 수 있는데요,

 

value 는

base.rx.controlPropertyWithDefaultEvents 의

리턴값이라고 하니 다시 한번 타고 들어가 Definition을 확인해줍시다

 

 

오호

editingEvents를 보니

[.allEditingEvents , .valueChanged]

이벤트가 발생했을 때 텍스트필드 값이

리턴된다는 것을 알 수 있네요

 

.allEditingEvents

All editing touches for UITextField objects.

=> 텍스트필드에 관한 터치 발생 시 이벤트 발생

 

 

.valueChanged

 

A touch dragging or otherwise manipulating a control,

causing it to emit a series of different values.

=> 드래깅 혹은 다른 방법을 통해 값이 변경되었을 때 이벤트 발생

 

 

그니까 정리하자면

rx.text 는 textfield 입력값이

변경되었을 때만 이벤트가 발생하는 것이 아니라

키보드를 올리고 내리기 위한 행동들에도

이벤트가 발생한다는 말입니다.

 


 

나는 text 문자열이 변경될 때만

값을 얻고 싶은데...

 

이 때 사용할 수 있는 Operator가 바로

.distinctUntilChanged 입니다 !!

 

이 연산자는 앞서 소개했듯

이전 이벤트 값과 변경된 현재 이벤트 값이

다를 때에만 리턴해주기 때문이죠 !

 

연산자를 적용하고 다시 출력해볼까요 ?

 

mainView.userIdTextField.rx.text
            .distinctUntilChanged()
            .subscribe { text in
                print(text)
            }
            .disposed(by: disposeBag)
            
/*
next(Optional("A"))
next(Optional("As"))
next(Optional("Asd"))
next(Optional("Asdf"))
*/

 

키보드를 올릴 때 발생했던 이벤트와

내릴 때 발생했던 이벤트의

text 값이 출력되지 않았습니다 ㅎㅎ

 


만약 여기서 Optional이 신경쓰인다면

옵셔널을 걸러주는 연산자

.orEmpty를 함께 써주면 된답니다 ~

 

mainView.userIdTextField.rx.text
            .orEmpty
            .distinctUntilChanged()
            .subscribe { text in
                print(text)
            }
            .disposed(by: disposeBag)
            
/*
next(A)
next(As)
next(Asd)
next(Asdf)
*/