티스토리 뷰
이번엔 Subject에 대해 알아보겠습니다.
RxSwift의 Subject는 Observable과 Observer의 역할을 모두 수행할 수 있습니다.
즉, 우리는 Subject를 통해서 event를 받을 수도 있고 event를 전달할 수도 있습니다.
RxSwift에는 4가지의 Subject 유형이 있습니다.
- PublishSubject : 비어있는 상태로 생성되고 그 후 전달되는 event들을 모두 구독자에게 전달
- BehaviorSubject : 초기 값을 가지고 생성되며, 그 후 전달되는 event가 있으면 그중 마지막 event를 (없으면 초기 값)을 구독자에게 전달
- ReplaySubject : 생성될 때 버퍼의 사이즈를 지정함. 버퍼 사이즈만큼의 최신 event 들을 저장하고 구독자에게 이를 전달
- AsyncSubject : Subject로 Error 또는 Completed 이벤트가 전달되었을 때, 그 직전 event를 구독자에게 전달
이렇게 글만 봐서는 이해가 어렵기 때문에 간단한 예제를 통해 학습해 보도록 하겠습니다!
PublishSubject
위 다이어그램에서 맨 윗 줄은 PublishSubject, 그리고 나머지는 구독자입니다.
위를 향하는 화살표는 구독자가 PublishSubject를 구독하는 시점, 아래를 향하는 화살표는 PublishSubject가 구독자에게 이벤트를 방출하는 것을 나타냅니다.
그럼 PublishSubject는 어떤 특징을 가졌을까요? 구독자가 Subject를 구독하는 시점 '이 후'에 발생한 모든 event를 구독자에게 '즉시' 전달합니다.
첫 번째 구독자 (두 번째 줄)는 PublishSubject에 event1이 발생한 이후에 구독을 시작했습니다. 그렇기에 event1은 전달받지 못하고 그 이후에 생성된 event2, event3은 모두 즉시 전달받게 됩니다.
그리고 두번째 구독자 (세 번째 줄)는 PublishSubject에 event1, event2가 발생한 이후에 구독을 시작했기 때문에 그 후 발생한 event3만 전달받게 됩니다.
그럼 위 그림을 코드로 작성해 보겠습니다.
import RxSwift
let bag = DisposeBag()
let publishSubject = PublishSubject<Int>()
// PublishSubject는 초기 값 없이 생성됨
publishSubject.onNext(1)
// publishSubject에 event1 발생
// 하지만 해당 Subject의 구독자가 없기때문에 아무도 event를 전달받지 못함
let subscriber1 = publishSubject.subscribe { print("subscriber1 :", $0)}
subscriber1.disposed(by: bag)
// 첫 번째 구독자 생성 및 publishSubject 구독
publishSubject.onNext(2)
// event2 발생
// Result ----------
// subscriber1 : next(2)
// 첫 번째 구독자에게 즉시 전달됨
let subscriber2 = publishSubject.subscribe { print("subscriber2 :", $0)}
subscriber2.disposed(by: bag)
// 두 번째 구독자 생성 및 publishSubject 구독
publishSubject.onNext(3)
// event3 발생
// Result ----------
// subscriber1 : next(3)
// subscriber2 : next(3)
// 첫번째, 두번째 구독자에게 즉시 전달됨
그럼 만약 PublishSubject에 Error 또는 Completed 이벤트가 발생한 후에 구독한 구독자들에겐 어떤 일이 발생할까요?
위 코드에 이어 작성해보겠습니다.
publishSubject.onCompleted()
// completed 이벤트 발생
let subscriber3 = publishSubject.subscribe { print("subscriber3 :", $0)}
subscriber3.disposed(by: bag)
// 세번째 구독자 생성 및 publishSubject 구독
// Result ----------
//subscriber1 : completed
//subscriber2 : completed
//subscriber3 : completed
위처럼 Completed 이벤트가 발생한 PublishSubject를 구독하면, completed 이벤트를 전달받게 됩니다.
Error 이벤트의 경우에도 같은 결과가 발생합니다.
BehaviorSubject
위 다이어그램에서 맨 윗 줄은 BehaviorSubject를 나타냅니다. 그리고 두 번째, 세 번째 줄은 구독자, 위로 향하는 화살표는 Subject를 구독하는 시점, 아래를 향하는 화살표는 event가 구독자에게 전달되는 것을 나타냅니다.
그럼 BehaviorSubject는 어떤 특징을 가지고 있을까요? BehaviorSubject는 초기 값이 저장된 채로 생성되고, 그 후 전달되는 event 중 가장 최신 이벤트로 저장된 값을 갱신합니다. 그리고 구독자가 Subject를 구독하는 시점에 가장 최신 이벤트를 전달하고, 그 후에 전달되는 모든 event는 즉시 구독자에게 전달 및 저장값을 경신합니다.
즉, BehaviorSubject는 구독자가 Subject를 구독한 이후에 event가 발생하지 않더라도 최소 1개의 이벤트를 전달받게 됩니다.
코드로 작성해보겠습니다.
let bag = DisposeBag()
let behaviorSubject = BehaviorSubject<Int>(value: 1)
// BehaviorSubject 생성 및 초기값으로 1 저장
let subscriber1 = behaviorSubject.subscribe { print("subscriber1 :", $0)}
subscriber1.disposed(by: bag)
// 첫 번째 구독자 생성 및 behaviorSubject 구독
// Result ----------
// subscriber1 : next(1)
// 초기값 1이 첫번째 구독자에게 전달됨
behaviorSubject.onNext(2)
// event2 발생
// Result ----------
// subscriber1 : next(2)
// 첫 번째 구독자에게 즉시 전달 및 behaviorSubject에 저장된 값을 2로 갱신함
let subscriber2 = behaviorSubject.subscribe { print("subscriber2 :", $0)}
subscriber2.disposed(by: bag)
// 두 번째 구독자 생성 및 behaviorSubject 구독
// Result ----------
// subscriber2 : next(2)
// 저장된 2가 두번째 구독자에게 전달됨
behaviorSubject.onNext(3)
// event3 발생
// Result ----------
// subscriber1 : next(3)
// subscriber2 : next(3)
// 첫번째, 두번째 구독자에게 즉시 전달됨 및 저장값 3으로 갱신
PublishSubject와 마찬가지로 Subject가 completed 이벤트가 발생한 후에 구독한 구독자에게는 completed 이벤트를 전달합니다.
behaviorSubject.onCompleted()
let subscriber3 = behaviorSubject.subscribe { print("subscriber3 :", $0)}
subscriber3.disposed(by: bag)
// Result ----------
//subscriber1 : completed
//subscriber2 : completed
//subscriber3 : completed
Error의 경우에도 마찬가지입니다.
ReplaySubject
위 다이어그램의 맨 윗줄은 버퍼의 크기가 2인 ReplaySubject입니다.
첫 번째 구독자 (두 번째 줄)는 이미 ReplaySubject를 구독하고 있는 상태이며, 그 후 발생한 event1, event2를 모두 즉시 전달받습니다. 그리고 두번째 구독자 (세번째 줄)는 구독한 시점에서 버퍼의 사이즈 만큼 저장된 event1, event2를 구독 즉시 전달받게 됩니다. 그 후 발생한 event3은 첫번째, 두번째 구독자 모두에게 즉시 전달되고 버퍼에 저장된 이벤트는 event2, event3으로 변경됩니다.
즉, ReplaySubject는 버퍼의 사이즈만큼의 최신 이벤트를 저장하고 저장된 event를 구독자에게 즉시 전달합니다. 또한, 새 이벤트를 받으면 버퍼에 담긴 이벤트 중 가장 오래된 이벤트를 갱신합니다.
코드를 작성해 보겠습니다.
let bag = DisposeBag()
let replaySubject = ReplaySubject<Int>.create(bufferSize: 2)
let subscriber1 = replaySubject.subscribe { print("subscriber1 :", $0)}
// 첫번째 구독자가 replaySubject 구독, 버퍼에 담긴 event가 없기에 아무것도 전달받지 못함
replaySubject.onNext(1)
// event1 발생 및 버퍼에 저장
// Result ----------
//subscriber1 : next(1)
// 구독중인 첫번째 구독자에게 event1 즉시 전달됨
replaySubject.onNext(2)
// event2 발생 및 버퍼에 저장
// Result ----------
//subscriber1 : next(2)
// 구독중인 첫번째 구독자에게 event2 즉시 전달됨
let subscriber2 = replaySubject.subscribe { print("subscriber2 :", $0)}
// 두 번째 구독자가 Subject 구독 시작
// Result ----------
//subscriber2 : next(1)
//subscriber2 : next(2)
// 두번째 구독자가 Subject를 구독하는 즉시 버퍼에 저장된 event1, event2 전달됨
replaySubject.onNext(3)
// event3 발생 및 버퍼에 있던 event1 (더 오래된 것)을 event3으로 교체
// Result ----------
//subscriber1 : next(3)
//subscriber2 : next(3)
// 구독중인 모든 구독자가 event3 발생 즉시 전달받음
그럼 Completed 또는 Error 가 발생한 후에 구독을 시작하면 어떻게 될까요?
replaySubject.onCompleted()
let subscriber3 = replaySubject.subscribe { print("subscriber3 :", $0)}
// Result ----------
//subscriber1 : completed
//subscriber2 : completed
//subscriber3 : next(2)
//subscriber3 : next(3)
//subscriber3 : completed
//이미 Subject를 구독중이던 구독자1, 구독자2는 즉시 completed 이벤트를 전달받고
//Subject가 Completed 된 이후에 구독을 시작한 구독자3은
//버퍼에 저장된 event2, event3을 전달 받은 후 completed 이벤트를 전달받음
BehaviorSubject는 저장된 값이 있어도 Completed 된 이후 구독자에게 Completed 이벤트만 전달하는 반면
ReplaySubject는 Completed 된 이후에 구독을 시작해도 버퍼에 저장된 이벤트들을 전달하고 Completed 이벤트를 전달합니다.
이는 Error의 경우에도 동일합니다.
AsyncSubject
AsyncSubject는 위의 Subject들과 이벤트를 전달하는 시점에 차이가 있습니다.
위의 Subject들은 구독 중에 이벤트가 발생하면 즉시 구독자에게 전달하는 공통점을 가지고 있습니다.
AsyncSubject는 Completd 이벤트가 발생했을 때만 직전에 전달받은 Next이벤트를 구독자에게 전달합니다.
만약 Completed 이벤트를 받지 못하고 계속 Next 이벤트만 받는 경우나 Error 이벤트로 Subject가 종료되는 경우에 구독자들은 어떤 이벤트도 전달받지 못합니다.
코드를 작성해보겠습니다.
let bag = DisposeBag()
let asyncSubject = AsyncSubject<Int>()
let subscriber1 = asyncSubject.subscribe { print("subscriber1 :", $0)}
// 첫번째 구독자가 asyncSubject 구독
asyncSubject.onNext(1)
asyncSubject.onNext(2)
// Next 이벤트가 발생해도 구독자는 전달받지 못함
asyncSubject.onCompleted()
// Result ----------
//subscriber1 : next(2)
//subscriber1 : completed
// Completed 이벤트가 발생하면 가장 최근에 발생한 event2를 구독자에게 전달하고 Completed도 전달
let subscriber2 = asyncSubject.subscribe { print("subscriber2 :", $0)}
// Result ----------
//subscriber2 : next(2)
//subscriber2 : completed
// Completed 이벤트가 발생한 후에 구독을 시작해도 Completed 전 가장 최신 이벤트를 전달받음
참고 : Reactive Programming with Swift By the raywenderlich.com Tutorial Team
'iOS > RxSwift' 카테고리의 다른 글
RxSwift - Filtering Operators (0) | 2022.05.15 |
---|---|
RxSwift - Relay (0) | 2022.03.05 |
RxSwift - Disposable, DisposeBag (0) | 2022.03.01 |
RxSwift - Infallible (0) | 2022.02.28 |
RxSwift - Observables, Observers (0) | 2022.02.27 |
- Total
- Today
- Yesterday
- 카카오인턴십
- 프로그래머스
- 안드로이드
- disposeBag
- SWEA
- rxswift
- boj
- 백준
- Swift
- 코코아팟
- Swift unowned
- cocoapods
- ios
- infallible
- blendshape
- Swift weak
- 백준온라인저지
- Neo4j
- 알고리즘
- ARKit
- Reactivex
- coreml
- GraphDB
- C++
- Lottie
- Kotlin
- rxswift6
- SwiftUI
- DispatchQueue
- blendshapes
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |