RxSwift - Relay와 Drive
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라고 합니다.