# Subject

옵저버블은 다른 옵저버블에 이벤트를 전달할 수 없다. 이와 동일하게 옵저버는 다른 옵저버를 구독할 수 없다.

서브젝트는 옵저버인 동시에 옵저버블이다. 따라서 다른 옵저버블을 구독할 수도 있고, 다른 옵저버에게 이벤트를 전달할 수도 있다.

서브젝트는 아래와 같은 종류가 있다.

  1. PublishSubject
  2. BehaviorSubject
  3. ReplaySubject
  4. AsyncSubject

이들 중 PublishSubjectBehaviorSubject를 래핑하는 PublishRelayBehaviorRelay도 있다.

# PublishSubject

가장 일반적인 형태의 서브젝트이다. PublishSubject<genericType>() 형태로 생성한다.

옵저버 생성은 subscribe 메서드를 통해 진행한다.

let subject = PublishSubject<String>()

// subject.onNext("불필요..") -> 구독 옵저버 생성 전 이벤트

let o1 = subject.subscribe{ print(">> ", $0) }
o1.disposed(by: disposeBag)

subject.onNext("HI")

let o2 = subject.subscribe{ print(">> ", $0) }
o2.disposed(by: disposeBag)

subject.onCompleted()

퍼블리시 서브젝트는 구독 이후의 이벤트만 처리한다. 서브젝트 객체 생성 시에는 이벤트를 등록하지 않고, 구독 이전에 이벤트를 발생시켜도 처리되지 않는다.

o1 옵저버에서는 HI 문자열에 대한 이벤트 출력이 이루어지지만, 그 이후에 새로 만들어진 o2 옵저버에서는 HI에 대한 이벤트 처리가 이루어지지 않게 된다.

서브젝트의 onCompleted 메서드를 호출하면 모든 옵저버에 onCompleted가 호출된다. 이는 에러 이벤트인 onError도 마찬가지이다.

enum MyError: Error {
   case error
}

subject.onError(MyError.error)

// 옵저버 모두에게 에러가 전달

퍼블리시 서브젝트는 옵저버가 구독한 이후 시점에 전달되는 이벤트에 대해서만 처리하게 된다는 주요 특징이 존재한다.

# Behavior Subject

퍼블리시 서브젝트는 생성시 내부 이벤트를 정의하지 않는다. 따라서 onNext 메서드를 통해 이벤트 정의를 새롭게 하지 않으면 옵저버 생성 후 구독을 진행해도 결과값을 받아볼 수 없다.

반면 BehaviorSubject는 생성자 함수에서 제네릭 타입에 대한 데이터를 가지고 이벤트를 추가하며 서브젝트를 생성하게 된다.

let publishSubject = PublishSubject<Int>()
publishSubject.subscribe{ print("Publish Subject >>", $0) } // 출력값 없음

let behaviorSubject = BehaviorSubject<Int>(value: 3)
behaviorSubject.subscribe{ print("Behavior Subject >>", $0) } // Behavior Subject >> next(3)

BehaviorSubject는 내부에 이벤트들을 저장하고 있다가 새로운 옵저버가 추가되는 경우 가장 최근 이벤트를 새로운 옵저버에 전달해준다.

// 현재 behaviorSubject에는 next(3) 이벤트가 저장되어 있음
let behaviorSubject = BehaviorSubject<Int>(value: 3)

// 1번째 옵저버
behaviorSubject.subscribe{ print("Behavior Subject >>", $0) }

behaviorSubject.onNext(4) // next(4) 이벤트 추가

// 2번째 옵저버
// 자동으로 next(4) 이벤트를 갖게된다.
behaviorSubject.subscribe{ print("Behavior Subject2 >>", $0) }

onCompleted 호출시 모든 구독자에게 onCompleted가 전달된다. 구독 전 정의된 이벤트에 대해 옵저버로 전달되지 않고 바로 completed된다. 이는 onError도 동일하다.

let behaviorSubject = BehaviorSubject<Int>(value: 3)
behaviorSubject.subscribe{ print("Behavior Subject >>", $0) }
behaviorSubject.onNext(4)

behaviorSubect.onCompleted() //  -> next(4) 이벤트가 두번째 구독자에게 전달되지 않고 바로 completed된다.
behaviorSubject.subscribe{ print("Behavior Subject2 >>", $0) }

# Replay Subject

behaviorSubject는 가장 최근 이벤트만 저장해두고 옵저버 생성시 전달한다. 나머지 이벤트들은 처리되지 않는다.

리플레이 서브젝트는 서브젝트 생성시 지정한 버퍼 사이즈만큼 이벤트를 저장해두고, 새로운 구독자 생성시 이들을 전달한다. 최근 이벤트를 먼저 전달하며 오래된 이벤트들이 삭제된다.

리플레이 서브젝트는 생성자로 생성하지 않고 bufferSize 파라미터와 함께 create 메서드로 생성한다.

let replaySubject = ReplaySubject<Int>.create(bufferSize: 3)
(1...10).forEach{ replaySubject.onNext($0) } // next(1) ... next(10) 이벤트가 전달되지만 next(8), next(9), next(10)만 저장

// next(8) ~ next(10)만 처리
replaySubject.subscribe{ print("ReplaySubject 1 >> ", $0) }
    .disposed(by: disposeBag)

// next(8) ~ next(10)만 처리
replaySubject.subscribe{ print("ReplaySubject 2 >> ", $0) }
    .disposed(by: disposeBag)

새로운 구독자 생성시 버퍼사이즈만큼 저장해둔 이벤트를 해당 구독자에게 전달하게 된다. 버퍼는 메모리 상에 관리되기 때문에 사용량에 주의해야 한다.

onCompleted, onError 이벤트 전달시 새로 생성된 구독자이더라도 버퍼에 저장된 이벤트 처리는 사라지지 않고 처리된 이후 사라진다.

# AsyncSubject

어싱크 서브젝트는 onCompleted이벤트가 전달되기 전까지 어떠한 이벤트도 구독자에게 전달하지 않는다.

onCompleted가 전달되는 시점에 가장 최근 이벤트만 구독자에게 전달한다. onError가 전달되는 경우 다른 이벤트는 전달되지 않는다.

let asyncSubject = AsyncSubject<Int>()

asyncSubject.subscribe{ print("AsyncSubject >>", $0) }
    .disposed(by: bag)

asyncSubject.onNext(1)
asyncSubject.onNext(2)
asyncSubject.onNext(3)

asyncSubject.onCompleted() // next(3)만 전달됨

# PublishRelay

릴레이는 Next이벤트만 전달하며 서브젝트를 래핑하는 형태로 존재한다. onCompleted, onError는 전달하지 않으며, 따라서 구독자가 disposed되기 전까지 이벤트를 계속해서 처리한다.

릴레이 사용시 RxCocoa 프레임워크이므로 이를 임포트 해줘야 한다.

accept

서브젝트와 다르게 이벤트 처리시 onNext가 아닌 accept 메서드를 사용한다.

다음은 퍼블리시 릴레이를 생성하는 코드이다.

let publishRelay = PublishRelay<Int>()
publishRelay.subscribe{ print("Publish Relay >> ", $0) }
    .disposed(by: bag)

publishRelay.accept(3) // next(3)

# BehaviorRelay

아래는 behaviorRelay를 생성하는 코드이다.

let behaviorRelay = BehaviorRelay<Int>(value: 3)
behaviorRelay.subscribe{ print("Behavior Relay >>", $0) }
    .disposed(by: bag)

behaviorRelay.accept(4)
print(behaviorRelay.value)

next(3)next(4) 이벤트가 전달되었다. 해당 릴레이는 가장 최근 이벤트인 next(4)를 저장해둔다.

behaviorRelayvalue 속성에 접근하면 가장 최근 이벤트의 값을 가져올 수 있으며, 이는 get-only 속성이다.

# ReplayRelay

RxSwift 버전이 6으로 업데이트 되면서 ReplayRelay가 새롭게 추가되었다.

let replayRelay = ReplayRelay<Int>.create(bufferSize: 3)
(1...10).forEach{ replayRelay.accept($0) }

replayRelay.subscribe{ print("Replay Relay >> ", $0) }
    .disposed(by: bag)

릴레이 생성시 리플레이 서브젝트 생성과 동일하게 create 메서드에 버퍼사이즈 파라미터를 전달하여 생성한다. accept로 이벤트를 저장하고 구독자에게 전달한다.

이벤트 전달은 버퍼사이즈 기준 가장 최근 이벤트들만 전달하게 된다. 리플레이 서브젝트와 동작이 동일하다.