RxSwift의 Subjects

Subject

Observable은 이벤트를 전달할 수만 있고 다른 Observable을 구독할 수 없다. ObserverObservable을 구독할 수 있지만 이벤트를 전달할 수 없다. Subject는 이벤트를 전달할 수 있고, 다른 Observable을 구독할 수 있다. 즉 Observable이자 Observer인 셈이다.

PublishSubject

가장 기본적인 Subject로 생성 시점에는 아무런 값도 없기 때문에 구독이 시작되도 아무런 값을 방출하지 않는다. PublishSubject는 구독한 시점 이후부터 발생한 값을 방출한다.

let publishSubject = PublishSubject<String>()
publishSubject.onNext("Hello") // 구독자가 없기 때문에 무시
publishSubject.subscribe { print($0) } // 구독했으나, 구독한 시점 이후부터 발생한 값이 없기 때문에 무시
publishSubject.onNext("HI") // next(HI)

onCompleted() 혹은 onError(_:) 이벤트를 만나게 되면 더 이상 onNext(_:) 이벤트를 통해 새로운 값을 전달할 수 없으며, 이 이벤트 이후에는 새로운 구독이 발생해도 더 이상 값을 전달할 수 없고 마지막 상태만을 전달한다.

publishSubject.onCompleted() // completed
publishSubject.onNext("RxSwift") // 이미 completed 되었기 때문에 무시
publishSubject.subscribe { print($0) } // completed (새로운 구독이지만 이미 completed 상태이기 때문)

BehaviorSubject

PublishSubject와는 다르게 생성 시점에 값을 가지고 있으며, 구독이 시작됐을때 가지고 있는 값을 전달한다. BehaviorSubject는 새로운 구독자에게도 늘 마지막 값을 전달한다. 반면 PublishSubject처럼 completed 혹은 error 상태에서는 새로운 구독이 발생해도 값을 전달할 수 없고 마지막 상태를 전달한다.

let behaviorSubject = BehaviorSubject.init(value: 0)
behaviorSubject.subscribe { print($0) } // next(0)
behaviorSubject.onNext(1) // next(1)
behaviorSubject.subscribe { print($0) } // next(1)
behaviorSubject.onCompleted() // completed * 2
behaviorSubject.onNext(11) // 무시
behaviorSubject.subscribe { print($0) } // completed

ReplaySubject

BehaviorSubject는 최신 이벤트를 가지고 있다가 구독자에게 전달하기 때문에 최신 이벤트를 제외한 모든 이벤트는 사라진다. ReplaySubject는 이와 다르게 직접 bufferSize를 통해 최신 이벤트로부터 몇개의 이벤트를 저장할지 직접 설정할 수 있다. ReplaySubjectcreate(bufferSize:)를 통해 생성한다. 새로운 구독자가 생기면 지정된 bufferSize만큼 저장된 이벤트를 전달하지만, 이미 구독중인 구독자에게는 최신 이벤트만 전달한다. 또 bufferSize는 메모리를 사용하기 때문에 bufferSize에 신경써야 한다. completed 혹은 error 상태인경우 새로운 구독이 발생하면 저장된 이벤트를 전달하고 마지막으로 상태를 전달한다.

let replaySubject = ReplaySubject<Int>.create(bufferSize: 3)
replaySubject.subscribe { print($0) } // 아무런 이벤트도 없기 때문에 무시
replaySubject.onNext(1) // next(1)
replaySubject.onNext(2) // next(2)
replaySubject.onNext(3) // next(3)
replaySubject.onNext(4) // next(4)

replaySubject.subscribe { print("new subscribe \($0)") } // new subscribe next(2) -> new subscribe next(3) -> new subscribe next(4)

replaySubject.onCompleted() // completed, new subscribe completed
replaySubject.subscribe { print("last subscribe \($0)") } // last subscribe next(2) -> last subscribe next(3) -> last subscribe next(4) -> last subscribe completed

AsyncSubject

AsnycSubject는 completed 상태인 경우에만 마지막 이벤트를 구독자에게 전달한다. completed 상태가 되면 마지막 이벤트와 함께 completed 상태를 전달하며, error인 경우에는 completed와는 다르게 아무런 이벤트를 전달하지 않고 error만을 전달한다.

let asyncSubject = AsyncSubject<Int>()
asyncSubject.subscribe { print($0) }
asyncSubject.onNext(1) // 무시
asyncSubject.onNext(2) // 무시
asyncSubject.onCompleted() // next(2) -> completed

enum MyError: Error {
    case error
}

let asyncSubject2 = AsyncSubject<Int>()
asyncSubject2.subscribe { print("error case \($0)") }
asyncSubject2.onNext(10) // 무시
asyncSubject2.onNext(11) // 무시
asyncSubject2.onError(MyError.error) // error case error(error)

Relay

PublishRelayPublishSubject를 Wrapping하고 있고 BehaviorRelayBehaviorSubject를 Wrapping하고 있다. Relay는 이벤트를 받아 구독자에게 전달하지만, 오로지 next 이벤트만 전달할 수 있다. completed나 error는 전달받지도, 하지도 않기 때문에 구독자가 disposed되기전까지 무한정으로 살아있다. 이런 특징 때문에 주로 UI에 사용된다. RelaySubject와 다르게 onNext(_:)가 아닌 accept(_:)를 통해 새로운 이벤트를 전달한다. BehaviorSubjectvalue 속성에서 값을 읽을 수 있다. (읽기전용)

let publishRelay = PublishRelay<Int>()
publishRelay.subscribe { print($0) }
publishRelay.accept(5) // next(5)

let behaviorRelay = BehaviorRelay(value: 55)
behaviorRelay.accept(66)
behaviorRelay.subscribe { print($0) } // next(66)
behaviorRelay.accept(33) // next(33)
print(behaviorRelay.value) // 33 (읽기전용)