[새싹 iOS] 16주차_RxSwift Subject / CombineLatest

임승섭·2023년 11월 4일
0

새싹 iOS

목록 보기
31/45

Subject 종류


1. PublishSubject

  • 초기값이 없다

  • 구독 이후 전달받은 이벤트부터 반영된다

  • complete 이후의 이벤트는 반영되지 않는다

    func aboutPublishSubject() {
        let publish = PublishSubject<Int>()
    
        publish.onNext(20)
        publish.onNext(30)
    
        publish
            .subscribe(with: self) { owner , value in
                print("PublishSubject onNext - \(value)")
            } onError: { owner , error in
                print("PublishSubject onError - \(error)")
            } onCompleted: { owner in
                print("PublishSubject onCompleted")
            } onDisposed: { owner in
                print("PublishSubject onDisposed")
            }
            .disposed(by: disposeBag)
    
        publish.onNext(3)   // 요기부터 반영
        publish.onNext(45)
        publish.on(.next(9))
    
        publish.onCompleted()
    
        publish.onNext(200)
        publish.onNext(500)
    }


2. BehaviorSubject

  • 초기값이 있다 (선언 시 값을 넣어준다)

  • 구독하기 전 가장 마지막 이벤트를 반영한다 (버퍼처럼 가지고 있는다)

  • complete 이후의 이벤트는 반영되지 않는다

    func aboutBehaviorSubject() {
        let behavior = BehaviorSubject(value: 100)
    
        behavior.onNext(20)
        behavior.onNext(30) // 얘는 요게 찍히네!!
    
        behavior
            .subscribe(with: self) { owner , value in
                print("BehaviorSubject onNext - \(value)")
            } onError: { owner , error in
                print("BehaviorSubject onError - \(error)")
            } onCompleted: { owner in
                print("BehaviorSubject onCompleted")
            } onDisposed: { owner in
                print("BehaviorSubject onDisposed")
            }
            .disposed(by: disposeBag)
    
        behavior.onNext(3)
        behavior.onNext(45)
        behavior.on(.next(9))
    
        behavior.onCompleted()
    
        behavior.onNext(200)
        behavior.onNext(500)
    }


3. ReplaySubject

  • 초기값이 없다

  • 선언 시 버퍼 사이즈를 넣어준다

  • 구독 시점 기준 버퍼 사이즈만큼 이전 이벤트부터 반영된다

  • complete 이후의 이벤트는 반영되지 않는다

    func aboutReplaySubject() {
        let replay = ReplaySubject<Int>.create(bufferSize: 3)
    
        replay.onNext(1)
        replay.onNext(2)
        replay.onNext(3)
        replay.onNext(4)
        replay.onNext(5)    // 요기부터 찍힘!! <- 구독한 시점 기준으로 버퍼 사이즈만큼
    
        replay.onNext(20)
        replay.onNext(30)
        replay
            .subscribe(with: self) { owner , value in
                print("ReplaySubject onNext - \(value)")
            } onError: { owner , error in
                print("ReplaySubject onError - \(error)")
            } onCompleted: { owner in
                print("ReplaySubject onCompleted")
            } onDisposed: { owner in
                print("ReplaySubject onDisposed")
            }
            .disposed(by: disposeBag)
    
        replay.onNext(3)
        replay.onNext(45)
        replay.on(.next(9))
    
        replay.onCompleted()
    
        replay.onNext(200)
        replay.onNext(500)
    }


4. AsyncSubject

  • 초기값이 없다

  • 구독 전 이벤트는 반영되지 않는다

  • complete 직전 이벤트 하나만 반영된다

  • complete 이후의 이벤트는 반영되지 않는다

    func aboutAsyncSubject() {
        let async = AsyncSubject<Int>()
    
        async.onNext(1)
        async.onNext(2)
        async.onNext(3)
        async.onNext(4)
        async.onNext(5)
    
        async.onNext(20)
        async.onNext(30)
        async
            .subscribe(with: self) { owner , value in
                print("AsyncSubject onNext - \(value)")
            } onError: { owner , error in
                print("AsyncSubject onError - \(error)")
            } onCompleted: { owner in
                print("AsyncSubject onCompleted")
            } onDisposed: { owner in
                print("AsyncSubject onDisposed")
            }
            .disposed(by: disposeBag)
    
        async.onNext(3)
        async.onNext(45)
        async.on(.next(9)) // 요기만 찍힘!
    
        async.onCompleted()
    
        async.onNext(200)
        async.onNext(500)
    }



CombineLatest


  • 두 개의 Sequence를 하나의 Sequence로 엮는다
  • 서로 다른 타입의 Sequence를 받을 수 있고, 다른 타입의 Sequence를 만드는 것도 가능하다

예시

let email = emailTextField.rx.text.orEmpty
let password = passwordTextField.rx.text.orEmpty

let validation = Observable.combineLatest(email, password) { first , second in
    return first.count > 8 && second.count > 6
}

validation
    .bind(to: signInButton.rx.isEnabled)
    .disposed(by: disposeBag)

validation
    .subscribe(with: self) { owner , value in
        owner.signInButton.backgroundColor = value ? UIColor.blue : UIColor.red
        owner.emailTextField.layer.borderColor = value ? UIColor.blue.cgColor : UIColor.red.cgColor
        owner.passwordTextField.layer.borderColor = value ? UIColor.blue.cgColor : UIColor.red.cgColor
    }
    .disposed(by: disposeBag)

BehaviorSubject vs. PublishSubject

  • CombineLatest 는 엮어주는 두 Sequence에 모두 최초 이벤트가 있어야
    합쳐진 Sequence에서 이벤트가 발생한다.
    즉, 초기값이 없는 경우 이벤트가 발생하지 않는다.

1. BehaviorSubject 사용

  • 선언할 때 넣어준 초기값부터, onNext로 전달하는 이벤트들도 잘 반영된다

    func testCombineLatest() {
        let a = BehaviorSubject(value: 2)
        let b = BehaviorSubject(value: "가")
    
        Observable.combineLatest(a, b) { first , second in
            return "결과 : \(first) & \(second)"
        }
        .subscribe(with: self) { owner , value in
            print(value)
        }
        .disposed(by: disposeBag)
    
        a.onNext(100)
        a.onNext(200)
        a.onNext(300)
    
        b.onNext("감")
        b.onNext("남")
        b.onNext("담")
    }

2. PublishSubject 사용

  • 두 subject 객체에 모두 값이 지정되기 전까지 subscribe가 일어나지 않는다

    func testCombineLatest() {
        let a = PublishSubject<Int>()
        let b = PublishSubject<String>() 
    
        Observable.combineLatest(a, b) { first , second in
            return "결과 : \(first) & \(second)"
        }
        .subscribe(with: self) { owner , value in
            print(value)
        }
        .disposed(by: disposeBag)
    
        a.onNext(100)
        a.onNext(200)
        a.onNext(300)
    
        b.onNext("감")
        b.onNext("남")
        b.onNext("담")
    }


RxCocoa

  • 최초 이벤트가 없을 때는 CombineLatest 이벤트가 발생하지 않는다는 건 알겠다.
  • Q. 그럼 맨 처음에 예시로 든 TextField.rx.text.orEmpty 는 초기값을 넣지 않았는데, 왜 이벤트가 잘 실행되었을까?
  • A. TextField.rx.text.orEmpty 는 초기값이 없는 것이 아니고, 빈 문자열을 이벤트로 전달하기 때문에 정상적으로 combineLatest 이벤트가 실행된다
  • Q. 그럼 RxCocoa에서 제공하는 위와 같은 친구들은 모두 초기값이 있는 거라고 봐도 될까?

  • A. 그렇지 않더라.

    • 궁금해서 Button.rx.tap 을 두 개 만들어서 똑같이 테스트했는데,
      하나의 버튼만 클릭했을 때는 이벤트가 발생하지 않고 나머지 버튼을 클릭한 순간
      정상적으로 combineLatest 이벤트가 발생함을 확인할 수 있었다.
    
    let a = signInButton.rx.tap
    let b = signUpButton.rx.tap
    
    let emit = Observable.combineLatest(a, b) { first , second in
        print("탭탭 컴바인 레이티스트")
    }
    emit.subscribe(with: self) { owner , _ in
        print("탭탭 섭스크라이브")
    }
    .disposed(by: disposeBag)


0개의 댓글