RxSwift 정리 2 - SubJect, operators(기본)

2021. 8. 20. 11:37Developer.TokkiSea/Apple

반응형

 

메모용

연산자는 너무너무도 많아서

새로운 연산자 마다 별도로 작성할 예정입니다.

여기서는 많이쓰는 기본적인것만 작성합니다.

.ignoreElements().elementAt().filter().skip().skipWhile().skipUntil().take().takeWhile().takeLast().takeUntil().distinctUntilChanged().distinctUntilChanged().delay.buffer.throttle.debounce.toArray().map.enumerated()

 

 

 

RxSwift의 장점 !!

비동기화 프로그래밍을 쉽고 강력하게 할 수 있다.

Rx 가 붙은 다른 언어로 즉시 사용할 수 있다.

RxSwift 작성 -> RxJava로 즉시 사용 가능

 

PublishSubject

let pSubject = PublishSubject<String>()

 

Subject 는 Observable과 Observer 역활을 동시에 수행합니다.

PublishSubject : subscribe가 있어도 onNext 시점 부터 전달이 됩니다.

onNext 이후에 다른 subscribe를 넣어도 이전값은 구독되지 않습니다.

onComplete, onError 이 발생했다면

새로운 subscribe는 onComplete, onError가 바로 발생한다.

 

pSubject.onNext("test 1")

형태로 요소를 추가하면

subscribe에 즉시 전달 된다.

 

BehaviorSubject

let bSubject = BehaviorSubject(score: BehaviorSubject<Int>(value: 90))

BehaviorSubject : 반드시 초기값을 가져야 한다.

subscribe 발생 시 가장 마지막 이벤트를

받고 시작한다.(90이 바로 발생)

bSubject.onNext(100)

만약 구독 이전에 onNext가 있었다면

그 위치부터 subscribe한다. (100만 바로 발생)

onComplete, onError 발생 이후 subscribe하면

역시나 onComplete, onError 가 바로 발생한다.

 

ReplaySubject

        let rSubject = ReplaySubject<String>.create(bufferSize: 3)

이전 발생한 값을 저장할 크기를 정한다.

그리고 그 크기만큼의 이전 값을 발생시킨다.

만약 3개의 값을 전달 받은 다음 onComplete, onError 발생 시키고

새로운 구독을 하면 이전 3개의 값 + onComplete, onError 가 발생된다.

 

xSubject.dispose()

Subject subscribe 중단

 

PublishRelay, BehaviorRelay

PublishSubject 와 BehaviorSubject 와 동일하다.

단, .next 만 발생되고, .completed, .error 는 무시된다.

즉, 끝나지 않게끔 만들수 있다.

  let relay = PublishRelay<String>()
  relay.accept("Knock knock, anyone home?")
  relay
    .subscribe(onNext: {
      print($0)
    })
    .disposed(by: disposeBag)
  relay.accept("1") // print: 1
 
// relay.accept(MyError.anError) 사용 불가 
// relay.onCompleted() 사용 불가

 

.ignoreElements()

onNext가 발생되지 않는다.

단, onComplete() , onError() 은 발생됨

 

.elementAt()

설정한 인덱스만 발생 시킨다.

pSubject
    .elementAt(2)
    .subscribe(onNext: { item in
        print(item)
    })
    .disposed(by: disposeBag)

strikes.onNext("first")
strikes.onNext("second")
strikes.onNext("third") // print : third

 

.filter()

조건문에 맞아야 발생 시킨다.

rSubject
.filter { // 조건문으로 걸러낸다.
    $0 != "" // 공백이 아닐것
}
.filter { string in
	return string.count > 4 // 크기가 5이상일것 
}
.subscribe {
	print($0)
}

r.Subject.onNext("testsize 11")

r.Subject.onNext("test")

결과 : "testsize 11"

 

.skip()

지정 위치부터 구독 시작.

Observable.of("A", "B", "C", "D", "E", "F")
.skip(3)
.subscribe(onNext: {
  print($0)
}) // D, E, F

 

.skipWhile()

filter과 비슷해보이지만 skip 은 계속 skip 하다가

조건이 만족된 이후로는 상관없이 계속 발생된다.

Observable.of(2, 2, 2, 3, 4, 4, 2)
.skipWhile { $0 % 2 == 0 }
.subscribe(onNext: {
    print($0)
}) // 3, 4, 4, 2

 

.skipUntil()

trigger 이 발생 한 이후에만 발생됨.

let subject = PublishSubject<String>()
let trigger = PublishSubject<String>()
subject
  .skipUntil(trigger)
  .subscribe(onNext: {
    print($0)
  })
  .disposed(by: disposeBag)
  subject.onNext("A")
  subject.onNext("B")
  trigger.onNext("X")
  subject.onNext("C")
  // C

 

.take()

정해진 개수만 발생한되 이후로는 모두 무시

Observable.of(1, 2, 3, 4, 5, 6)
.take(3)
.subscribe(onNext: {
  print($0)
}) // 1,2,3

.takeWhile()

Observable.of(2, 3, 4, 4, 6, 6, 2)
.enumerated()
.takeWhile { index, integer in
  integer % 2 == 0 && index < 3
}
.map { $0.element }
.subscribe(onNext: {
  print($0)
})  // 2, 4 (인텍스가 3이하인것중 2로 나눠지는것)

 

.takeLast()

완료 후 발생 마지막 부터 개수만큼 발생(테스트 필요)

.takeUntil()

.skipUntil() 의 반대 인것 처럼 트리거 발생 시점부터는 발생되지 않는다.

.takeUntil(self.rx.deallocated)

처럼 발생 요소가 없어지면 구독을 종료할때 사용된다.

 

.distinctUntilChanged()

연속 중복값 방지

Observable.of("A", "A", "B", "B", "A")
.distinctUntilChanged()
.subscribe(onNext: {
  print($0)
})  // A, B, A

 

.distinctUntilChanged()

입력된 숫자를 영어로 변환한 다음 동일한 발음이 존재하면 무시(return true) 예제

let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
Observable<NSNumber>.of(10, 110, 20, 200, 210, 310)
    .distinctUntilChanged { a, b in
        guard let aWords = formatter.string(from: a)?.components(separatedBy: " "),
            let bWords = formatter.string(from: b)?.components(separatedBy: " ")
            else {
                return false
        }
        var containsMatch = false
        for aWord in aWords where bWords.contains(aWord) {
            containsMatch = true
            break
        }
        return containsMatch
}
.subscribe(onNext: {
    print($0)
}) // 10, 20, 200

두값씩 비교해야 해서 a, b 가 발생한다.(또는 $0, $1)

10 이 발생된다. 이전값이 없으므로(초기값도 줄수는 있다.)

a = 10 = "ten" , b = 110 = "one hundred ten"

"ten" 이 "one hundred ten" 에도 있기에 110을 무시

두번째 a = 20 = "twenty" , b = 200 = "two hundred"

겹치지 않으므로 20 발생

그리고 200을 무시 하지 않기 때문에 다음 a 값이 200이된다.

a = 200 = "two hundred" , b = 210 = "two hundred ten"

"two", "hundred"가 겹치므로 210이 무시되고

200 발생 210이 무시 되었기에 여전히 a 값은 200 이다.

a = 200 = "two hundred" , b = 310 = "three hundred ten"

"hundred"가 겹치므로 310도 무시

 

.delay

말그대로 시간 지연

.delay(DispatchTimeInterval.seconds(3), scheduler: MainScheduler.instance)

 

.buffer

시간과 개수따라 발생

2초가 경과 하거나, 2개이면 발생

단, 이건 테스트가 조금 필요함 향후 수정예정

2초가 경과하면 1개든 2개든 무조건 발생될것이다.

2초 이내에 2개가 체워지면 바로 발생 된 후

2초가 경과 될때까지 기다릴것인가?

2초에 2개를 모아 발생 시키는데 짧은

시간에 많이 발생 되면 꾸준히 모아서 2초에 2개씩만 발생 되는건가?

.buffer(timeSpan: DispatchTimeInterval.milliseconds(2000), count: 2, scheduler: MainScheduler.instance)

 

 

.throttle

발생 간격을 0.3초로 해준다.

 

 

3, 4 발생

latest true 로 설정할 경우 마지막 발생을 사용하므로

1, 3, 4, 5가 추가로 발생

.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.throttle(.milliseconds(300), latest: true, scheduler: MainScheduler.instance)

 

.debounce

지정 시간 이후 가장 최근것을 통과 시킨다.

.debounce(.milliseconds(300), scheduler: MainScheduler.instance)

 

.toArray()

배열로 만듬

Observable.of("A", "B", "C")
.toArray()
.subscribe(onNext: {
  print($0)
}) // ["A", "B", "C"]

 

.map

요소들의 값을 변화 시킨다.

let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
Observable<Int>.of(1, 2, 3)
  .map {
    formatter.string(for: $0) ?? ""
  }
  .subscribe(onNext: {
    print($0)
  }) // one, two, three

결과 : "testsize 11 added string" , "test added string"

 

.enumerated()

index와 같이 발생 된다.

Observable.of(1, 2, 3, 4, 5, 6)
.map { integer in
  integer * 2
}
.subscribe(onNext: {
  print($0)
})
.disposed(by: disposeBag)
// 2, 4, 6, 8, 10, 12

Observable.of(1, 2, 3, 4, 5, 6)
.enumerated()
.map { index, integer in
  index > 2 ? integer * 2 : integer
}
.subscribe(onNext: {
  print($0)
})
.disposed(by: disposeBag)
// 1, 2, 3, 8, 10, 12

 

 

 

 

반응형