RxRelay? - Relay와 Subject의 차이
Relay는 Subject의 Wrapper 클래스로, PublishRelay와 BehaviorRelay가 있습니다. BehaviorRelay의 소스 코드를 확인해보면서 공부해보도록 하겠습니다.
public final class BehaviorRelay<Element>: ObservableType {
private let subject: BehaviorSubject<Element>
/// Accepts `event` and emits it to subscribers
public func accept(_ event: Element) {
self.subject.onNext(event)
}
/// Current value of behavior subject
public var value: Element {
// this try! is ok because subject can't error out or be disposed
return try! self.subject.value()
}
/// Initializes behavior relay with initial value.
public init(value: Element) {
self.subject = BehaviorSubject(value: value)
}
/// Subscribes observer
public func subscribe<Observer: ObserverType>(_ observer: Observer) -> Disposable where Observer.Element == Element {
self.subject.subscribe(observer)
}
/// - returns: Canonical interface for push style sequence
public func asObservable() -> Observable<Element> {
self.subject.asObservable()
}
/// Convert to an `Infallible`
///
/// - returns: `Infallible<Element>`
public func asInfallible() -> Infallible<Element> {
asInfallible(onErrorFallbackTo: .empty())
}
}
BehaviorSubject를 감싸고 있는 Wrapper 클래스네요.
Subject에서 이벤트를 방출할 때 사용하는 onNext 대신 Relay에서는 accept 함수를 사용하는군요.
Subject와 마찬가지로 value()를 통해 프로퍼티에 subscribe한 값을 가져올 수 있군요.
흠.. 그럼 이벤트 방출 시 사용하는 함수 이름 말고는 차이가 없는건가?
아닙니다.
Subject와 달리 Relay는 complete와 error을 발생시킬 수 없습니다. 그래서 Relay는 UI 작업을 하기에 적합하죠.
그럼 왜 Wrapper 클래스로 감싸고, 어떻게 이를 사용할 수 있을까요?
Driver
Relay는 앞서 살펴봤듯 observer이자 observable한 Subject의 Wrapper 클래스입니다. Relay를 관찰할 때는 observable의 Wrapper 클래스인 Driver 형태로 관찰할 수 있습니다.
기본적으로 Observable은 Background Thread에서 동작하기 때문에, Main Thread에서 작업하고 싶을 때 Observable 대신 Driver을 사용합니다. 즉, UI 작업을 하기 적합하죠.
Driver로 변환할 때는 asDriver() 메서드를 호출하면 됩니다.
let relay = BehaviorRelay<Int>(value: 10)
let driver = relay.asDriver()
그럼 asDriver()가 어떻게 구성되어 있는지 확인해보겠습니다. 해당 파일은 BehaviorRelay 파일이 아닌 extension에 구현되어 있습니다.
extension BehaviorRelay {
/// Converts `BehaviorRelay` to `Driver`.
///
/// - returns: Observable sequence.
public func asDriver() -> Driver<Element> {
let source = self.asObservable()
.observe(on:DriverSharingStrategy.scheduler)
return SharedSequence(source)
}
}
여기서 DriverSharingStrategy를 따라 들어가면, scheduler가 나오고 SharingScheduler를 또 따라 들어가면 make 함수가 다음과 같이 MainScheduler()로 작성되어 있는 것을 확인할 수 있습니다.
/* BehaviorRelay+Driver.swift */
public struct DriverSharingStrategy: SharingStrategyProtocol {
public static var scheduler: SchedulerType { SharingScheduler.make() }
public static func share<Element>(_ source: Observable<Element>) -> Observable<Element> {
source.share(replay: 1, scope: .whileConnected)
}
}
/* SchedulerType+SharedSequence.swift */
public private(set) static var make: () -> SchedulerType = { MainScheduler() }
이렇게 Driver을 생성하며, asDriver를 통해 Driver이라는 리턴 타입을 확인할 수 있습니다.
생성한 Driver를 구독하기 위해서는 Observable을 구독할 때의 subscribe(onNext: {})와 마찬가지로 drive(onNext: {})를 사용합니다.
driver
.drive(onNext: {
print($0)
})
.disposed(by: disposeBag)
그럼 꼭 Relay를 구독하기 위해서는 Drive를 사용해야하나?
아닙니다. 앞서 Relay 소스코드에서 보았듯이 subscribe 메서드가 정의되어 있었죠. driver은 UI 작업과 같이 메인 스레드에서 작업하고 싶을 때 사용합니다. 만약 메인 스레드에서 작업하고 싶지 않은 경우에는 subscribe를 통해 구독할 수 있습니다.
Driver은 UI 작업을 위해 사용되는데, 이렇게 UI 처리에 특화된 Observable을 Traits라고 합니다.
' iOS > RxSwift' 카테고리의 다른 글
RxSwift - extension Reactive는 무엇일까? (0) | 2023.01.19 |
---|---|
RxSwift - Error 관리 (0) | 2022.11.07 |
RxSwift - TimeBasedOperators (0) | 2022.11.07 |
RxSwift - Sequence 내부의 요소들간의 결합 연산자(reduce, scan) (0) | 2022.11.07 |
RxSwift - 하나의 Observable 가 Trigger 역할 후 Observable 들을 조합하는 방법(withLatestFrom, sample, amb, switchLatest) (0) | 2022.11.06 |