Observable만큼 중요한 개념이 Subject이다. 누군가에게 event를 받고 subscriber에게 그 결과를 전달한다.
Subject의 종류로는 Publish Subject
, Behavior Subject
, Replay Subject
, Variable
, BehaviorRelay
가 있다. 하나하나씩 살펴보자.
Publish Subject는 subscribe한 시점 이후에 발생되는 event만 전달받아 subscriber에게 그 결과를 전달한다.
먼저 코드를 보면서 이해해보자. 코드는 같은 파일에 있다고 가정하며 설명하는 순서대로 코드가 작성되어 있다.
import UIKit
import RxSwift
let disposeBag = DisposeBag()
let subject = PublishSubject<String>()
subject.onNext("Issue 1")
위 코드와 같이 PublishSubject를 생성하면 String 타입의 event만 방출할 수 있다.
이대로 실행하면 아무일도 일어나지 않는다. Why? subscriber가 없기 때문.
이 의미는 다음과 같다. 내가 잡지 회사를 갖고 있고 새로운 잡지를 발행했는데 구독자가 아무도 없기 때문에 아무것도 발생하지 않는거와 동일하다.
그럼 subscribe을 한다면 원하는 결과가 나올까?
subject.subscribe { event in
print(event)
}
지금도 별 차이를 못 느낄 것이다. 즉, call한 subscription을 여전히 못 볼 것이다.
Why? event를 create한 이후에 subscription을 create했기 때문.
즉, Publish Subject는 현재 나를 subscribe하는 subscriber들에게만 event를 방출한다.
그럼 subscription을 create한 이후에 event를 생성하면 원하는 결과를 얻을 수 있다.
subject.onNext("Issue 2")
subject.onNext("Issue 3")
결과
next(Issue 2)
next(Issue 3)
그 후, dispose하고 event를 생성한다면 어떻게 될까?
subject.dispose()
subject.onNext("Issue 4")
당연하게도 구독을 끊었기 때문에 당연히 이런 event는 무시된다.
만약 잡지를 구독하다가 구독을 해지했는데 새로운 잡지가 발행된다면 받을 수 있을까? 당연히 받을 수 없다.
subject는 value를 방출할 수 있고 observe 할 수 있기 때문에 subscriber이고 observer 역할을 하는 것을 잊지 말자.
Publish Subject와 매우 비슷하지만 한가지 차이점이 있다. 초기화할 때 반드시 초기값을 줘야한다. Why? subscribe할 때 반드시 가장 최신의 value를 방출하기 때문. 즉, subscribe가 발생하면 발생한 시점 이전에서 가장 최신의 event를 전달받는다.
let subject = BehaviorSubject(value: "Initial value")
subject.subscribe { event in
print(event)
}
결과
next(Initial value)
그 이후에 event를 생성하면?
subject.onNext("Issue 1")
결과
next(Initial value)
next(Issue 1)
subscribe할 때를 기준으로 가장 최신의 event도 같이 방출하는 것을 알 수 있다.
그럼 behavior subject의 장점은? 새로운 subscriber가 가장 최신의 event만을 알아야할 때 좋다는 것이다.
Replay Subject는 우리가 설정한 buffer size를 바탕으로 event를 replay한다.
let subject = ReplaySubject<String>.create(bufferSize: 2)
이 의미는 새로운 subscriber가 이 subject를 subcribe할 때, 자동적으로 subject에 의해 방출된 가장 최신의 value 2개를 replay한다는 것이다.
subject.onNext("Issue 1")
subject.onNext("Issue 2")
subject.onNext("Issue 3")
어떤 일이 일어날까? 우리는 이 subject에 대한 subscription이 없기 때문에 아무일도 안일어난다.
아시다시피, 단지 event만 방출한거다. 하지만 이것에 대해 듣는 사람은 아무도 없다.
So, 이것을 subscribe할거다.
subject.subscribe {
print($0)
}
결과
next(Issue 2)
next(Issue 3)
buffer size만큼 나오는 것을 알 수 있다.
그럼 새로운 subscriber를 만들면?
subject.onNext("Issue 4")
subject.onNext("Issue 5")
subject.onNext("Issue 6")
print("[Subscription 2]")
subject.subscribe {
print($0)
}
결과
[Subscription 2]
next(Issue 5)
next(Issue 6)
새로운 subscriber를 위해 가장 최신의 value를 replay한다.
So, replay subject는 만약 buffer size에 기반해 replay를 원할 때 좋다. 예를 들어 최근 검색한 기록 중 몇개만 보고싶을 때이다.
value property를 사용하여 Subject의 현재 값에 접근할 수 있다. 또한 value property를 통해 새로운 값을 추가할 수 있다. onNext(_: ) 메서드는 사용하지 않는다.
변수를 observable로 변환한 다음 변수를 구독할 수 있다. 즉, variable은 subject가 아니므로 asObservable()을 사용하여 subject에 접근한 뒤 subscribe한다.
let variable = Variable("Initial Value")
variable.value = "Hello World"
variable.asObservable()
.subscribe {
print($0)
}
결과
ℹ️ [DEPRECATED] `Variable` is planned for future deprecation. Please consider `BehaviorRelay` as a replacement. Read more at: https://git.io/vNqvx
next(Hello World)
결과에서 알 수 있듯이, 주목해야할 점은 Variable이 조만간 사라질 계획이다는 것이다. Variable의 대체로 BehaviorRelay를 사용하라고 한다.
String 타입뿐만 아니라 Array 타입도 가능하다.
let variable = Variable([String]())
variable.value.append("Item 1")
variable.asObservable()
.subscribe {
print($0)
}
결과
ℹ️ [DEPRECATED] `Variable` is planned for future deprecation. Please consider `BehaviorRelay` as a replacement. Read more at: https://git.io/vNqvx
next(["Item 1"])
pod 'RxCocoa', '~> 4.0'
BehaviorRelay를 사용하기 위해 위와 같이 의존성을 추가해주고 RxCocoa
를 import해준다.
코드를 보자.
let relay = BehaviorRelay(value: "Initial Value")
relay.asObservable()
.subscribe {
print($0)
}
결과
next(Initial Value)
Variable과 차이가 없어 보인다.
But, BehaviorRelay의 value를 바꿀 때 그 차이를 알 수 있다.
relay.value = "Hello World"
결과
Cannot assign to property: 'value' is a get-only property
value가 읽기 전용이므로 변경할 수 없다는 에러를 내뱉는다.
그럼 어떻게 해야 할까? 기존 값을 변경할 수는 없지만 새로운 값을 할당할 수 있다.
relay.accept("Hello World")