ย Subject
Subject๋ โObservable์ด์ Observerโ๋ผ๊ณ ๋ง์ด๋ค ์๊ณ ์์ต๋๋ค.
์ด๋ Observable๋ก์ ๋ฐ์ดํฐ๋ฅผ ํ์ฐ๊ณ ๋ค๋ Stream๋ฅผ ๊ฐ์ง๋ฉด์, Observer๋ก์ ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๋๊ฒ ๊ด์ฐฐ๋๋ฉด ํด๋น ๋ฐ์ดํฐ๋ฅผ Stream์ ํ์ด๋ค๋ ๊ฑธ ๋ปํฉ๋๋ค.
์ด์ ์ ์๊ธฐํ๋ RxSwift - Observable ์ ์์ฑ๋ Observable์ ์ด๋ฏธ ๋ฃ์ด์ง ๊ฐ๋ง ๋ณด๋ผ ์ ์์ต๋๋ค. ํ์ง๋ง, ์ด๋ฐ ๊ฒฝ์ฐ๊ฐ ์๊ธธ ์ ์์ต๋๋ค. ํ
์คํธ ํ๋์ ์๋ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ํด๋น ๊ฐ์ ๋ฐ์ดํฐ๋ก ๋ณด๋ด์ฃผ๋ ๊ฑฐ์ฃ . ํ
์คํธ ํ๋ ๊ฐ์ด ๋ณ๊ฒฝ๋๊ธฐ ์ ๊น์ง๋ ์ด๋ค ๊ฐ์ผ๋ก ๋ณ๊ฒฝ๋ ์ง ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ Observable์ ํด๋น ๊ฐ์ ๋ฏธ๋ฆฌ ๋๊ฒจ์ค ์ ์์ต๋๋ค. ๊ฐ์ด ๋ฐ๋๋์ง ๊ด์ฐฐํ๊ณ ์์ด์ผ ํ๋๊ฒ๋๋ค.
์ด๋ฐ ์ญํ ์ ํด์ค ์ ์๋ ๊ฒ์ด Subject์
๋๋ค.
Subject๋ฅผ ์์ฑํด๋๋ฉด ํ
์คํธ ํ๋๊ฐ ๋ฐ๋ ๋๋ง๋ค ๊ทธ ๊ฐ์ Subject Stream์ผ๋ก ๋ฐฉ์ถํ๊ฒ๋ํ์ฌ Observable์์ ํด๊ฒฐํ์ง ๋ชปํ๋ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค.
ย Subject์ ์ข ๋ฅ
PublishSubject
subscribe๋ ์์ ์ดํ๋ถํฐ ๋ฐ์ํ ์ด๋ฒคํธ ์ ๋ฌ
PublishSubject๋ โต ์์ฑ์ ์ด๊ธฐ ๊ฐ์ด ์๋ Subject ์
๋๋ค.
๊ตฌ๋
ํ ์์ ๋ถํฐ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ฉด ๊ฐ์ ๋ฐ์ ์ ์๊ณ , Subject๊ฐ ์์ ํ ์ข
๋ฃํ๊ณ ๋๋ฉด ์๋ก์ด ๊ตฌ๋
์๊ฐ ์๊ธด๋ค๊ณ ํด๋ ์๋ํ์ง ์์ต๋๋ค. ์ข
๋ฃ ํ์ ์๋ ๊ตฌ๋
ํ๊ณ ์๋ ๊ตฌ๋
์์ ์ข
๋ฃ ์ดํ ๊ตฌ๋
์ ๋ชจ๋์๊ฒ ์ข
๋ฃ ์ด๋ฒคํธ์ ์๋ฆผ์ ์ค๋๋ค.
let ๊ธ์์์ค๋ง = PublishSubject<String>()
let disposeBag = DisposeBag()
let ํ์A = ๊ธ์์์ค๋ง
.subscribe(onNext: { (food) in
print("ํ์A๊ฐ ๋ ๋ ๋จน์ ",food)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์A ์ด์ ๋")
})
.disposed(by: disposeBag)
๊ธ์์์ค๋ง.onNext("๋ง๋")
๊ธ์์์ค๋ง.onNext("์นํจ")
๊ธ์์์ค๋ง.onCompleted() // ์์ฃผ๋จธ๋ ํด๊ทผ
let ํ์B = ๊ธ์์์ค๋ง
.subscribe(onNext: { (food) in
print("ํ์B๊ฐ ๋ ๋ ๋จน์ ",food)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์B ์ด์ ๋")
})
.disposed(by: disposeBag)
๊ธ์์์ค๋ง.onNext("์คํ๊ฒํฐ")
๊ธ์์์ค๋ง.onNext("๊ณฑ์ฐฝ")
๊ธ์์์ค๋ง.onNext("๋๋ฐฉ์ด")
/*
ํ์A๊ฐ ๋ ๋ ๋จน์ ๋ง๋
ํ์A๊ฐ ๋ ๋ ๋จน์ ์นํจ
ํ์A ์ด์ ๋
ํ์B ์ด์ ๋
*/
Swift
๋ณต์ฌ
BehaviorSubject
subscribe๊ฐ ๋ฐ์ํ๋ฉด ์ฆ์ ํ์ฌ ์ ์ฅํ ๊ฐ์ ์ด๋ฒคํธ๋ก ์ ๋ฌ
BehaviorSubject๋ โต PublishSubject์ ๋น์ทํ์ง๋ง ์ด๊ธฐ๊ฐ์ ๊ฐ์ง๋๋ค.
BehaviorSubject๋ฅผ subscribeํ๋ฉด ์ฆ์ ์ด๊ธฐ๊ฐ์ ์ด๋ฒคํธ๋ก ์ ๋ฌํ๊ฒ ๋ฉ๋๋ค. ์ด๊ธฐ๊ฐ์ด ์๋ ์ํ์์๋ default๊ฐ์ ์ ๋ฌํ๊ณ , ์ด๊ธฐ๊ฐ์ด ์๋ค๋ฉด ์ต๊ทผ๊ฐ(ํญ์ ์ง์ ์ ๊ฐ๋ถํฐ ๊ตฌ๋
)์ ์ ๋ฌํฉ๋๋ค. ํ์ง๋ง, ์ด๊ธฐ๊ฐ์ด ํ์ํ์ง ์๋ค๋ฉด ๊ทธ๋ฅ PublishSubject๋ฅผ ์ฌ์ฉํ๋๊ฒ ์ข์ต๋๋ค.
BehaviorSubject๋ ๋ค๋ฅธ Subject์๋ ๋ค๋ฅด๊ฒ โถ ๋ง์ง๋ง ์ด๋ฒคํธ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ์์ต๋๋ค. ๋ฐ๋ผ์, ๊ฐ์ ์ ์ฅํด๋๊ณ ์ฌ์ฉํ๊ณ ์ถ์ ๋๋ BehaviorSubject๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
let ๊ธ์์์ค๋ง = BehaviorSubject(value: "๋๋ฐฉ์ด")
let disposeBag = DisposeBag()
let ํ์A = ๊ธ์์์ค๋ง
.subscribe(onNext: { (food) in
print("ํ์A๊ฐ ๋ ๋ ๋จน์ ",food)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์A ์ด์ ๋")
})
.disposed(by: disposeBag)
๊ธ์์์ค๋ง.onNext("์๊ณ ๊ธฐ")
๊ธ์์์ค๋ง.onNext("ํผ์")
๊ธ์์์ค๋ง.onNext("์ผ์ดํฌ")
let ํ์B = ๊ธ์์์ค๋ง
.subscribe(onNext: { (food) in
print("ํ์B๊ฐ ๋ ๋ ๋จน์ ",food)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์B ์ด์ ๋")
})
.disposed(by: disposeBag)
๊ธ์์์ค๋ง.onNext("์๋์์น")
๊ธ์์์ค๋ง.onNext("์ํ๋ ")
/*
ํ์A๊ฐ ๋ ๋ ๋จน์ ๋๋ฐฉ์ด /// default value ์ ๋ฌ
ํ์A๊ฐ ๋ ๋ ๋จน์ ์๊ณ ๊ธฐ
ํ์A๊ฐ ๋ ๋ ๋จน์ ํผ์
ํ์A๊ฐ ๋ ๋ ๋จน์ ์ผ์ดํฌ
ํ์B๊ฐ ๋ ๋ ๋จน์ ์ผ์ดํฌ /// ์ต๊ทผ ๊ฐ ์ ๋ฌ
ํ์A๊ฐ ๋ ๋ ๋จน์ ์๋์์น
ํ์B๊ฐ ๋ ๋ ๋จน์ ์๋์์น
ํ์A๊ฐ ๋ ๋ ๋จน์ ์ํ๋
ํ์B๊ฐ ๋ ๋ ๋จน์ ์ํ๋
*/
Swift
๋ณต์ฌ
ReplaySubject
n๊ฐ์ ์ด๋ฒคํธ๋ฅผ ์ ์ฅํ๊ณ subscribe๊ฐ ๋๋ ์์ ๊ณผ ์๊ด์์ด ์ ์ฅํ ๋ชจ๋ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌ
ReplaySubject๋ โต ๊ตฌ๋
์ด์ ์ ์์๋ ์ด๋ฒคํธ๋ฅผ ๋ค ๋ฐ์ ์ ์์ต๋๋ค. ์์ฑ ์์ ์ ํํ ํน์ ํฌ๊ธฐ๋งํผ ์ผ์์ ์ผ๋ก ์ด๋ฒคํธ๋ฅผ ์บ์ํ๊ฑฐ๋ ๋ฒํผ์ ์ ์ฅํด์ ์ต์ ์์๋ฅผ ๋ชจ๋ ๋ฐฉ์ถํฉ๋๋ค.
โถ ๋ฒํผ์ ํฌ๊ธฐ๋ ์ค์ ํ ์ ์์ผ๋ฉฐ, ๋ง์ฝ ๋ฒํผ์ ํฌ๊ธฐ๊ฐ 0์ด๋ฉด PublishSubject์ ๋์ผํ๊ฒ ์๋ํฉ๋๋ค. ๋ฐ์ดํฐ ํ์
์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํฌ๊ฒ ์ฐจ์งํ๋ ๊ฐ์ด๋ผ๋ฉด ๋ฉ๋ชจ๋ฆฌ ๋ถํ๊ฐ ๋ ์๋ ์์ต๋๋ค.
ReplaySubject๋ create(bufferSize: Int), createUnbounded ๋ฉ์๋๋ฅผ ๊ฐ์ง๋๋ฐ, ์ด๋ค ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋๋์ ๋ฐ๋ผ์ ๊ฒฐ๊ณผ๊ฐ ๋ฌ๋ผ์ง๋๋ค.
โข
create(bufferSize: Int)
let ๊ตฌ๋ชฌ์ ์๋ = ReplaySubject<String>.create(bufferSize: 2)
let disposeBag = DisposeBag()
๊ตฌ๋ชฌ์ ์๋.onNext("ํ์")
๊ตฌ๋ชฌ์ ์๋.onNext("์ํ")
๊ตฌ๋ชฌ์ ์๋.onNext("์์ด")
let ํ์A์๋ = ๊ตฌ๋ชฌ์ ์๋
.subscribe(onNext: { (subject) in
print("ํ์A๊ฐ ๋ฐฐ์ด ",subject)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์A ์ด์ ๊ณต๋ถ ๋")
})
.disposed(by: disposeBag)
let ํ์B์๋ = ๊ตฌ๋ชฌ์ ์๋
.subscribe(onNext: { (subject) in
print("ํ์B๊ฐ ๋ฐฐ์ด ",subject)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์B ์ด์ ๊ณต๋ถ ๋")
})
.disposed(by: disposeBag)
// ๊ตฌ๋ชฌ์ ์๋์ด ์์ด๋ค์๊ฒ ๋จ๋จํ ์๋ ค์ค ์ ์๋ ์์ด 2๊ณผ๋ชฉ์ผ๋ก ํ์
/*
ํ์A๊ฐ ๋ฐฐ์ด ์ํ
ํ์A๊ฐ ๋ฐฐ์ด ์์ด
ํ์B๊ฐ ๋ฐฐ์ด ์ํ
ํ์B๊ฐ ๋ฐฐ์ด ์์ด
*/
Swift
๋ณต์ฌ
โข
createUnbounded
let ๊ตฌ๋ชฌ์ ์๋ = ReplaySubject<String>.createUnbounded()
let disposeBag = DisposeBag()
๊ตฌ๋ชฌ์ ์๋.onNext("ํ์")
๊ตฌ๋ชฌ์ ์๋.onNext("์ํ")
๊ตฌ๋ชฌ์ ์๋.onNext("์์ด")
let ํ์A์๋ = ๊ตฌ๋ชฌ์ ์๋
.subscribe(onNext: { (subject) in
print("ํ์A๊ฐ ๋ฐฐ์ด ",subject)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์A ์ด์ ๊ณต๋ถ ๋")
})
.disposed(by: disposeBag)
let ํ์B์๋ = ๊ตฌ๋ชฌ์ ์๋
.subscribe(onNext: { (subject) in
print("ํ์B๊ฐ ๋ฐฐ์ด ",subject)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์B ์ด์ ๊ณต๋ถ ๋")
})
.disposed(by: disposeBag)
// ๊ตฌ๋ชฌ์ ์๋์ด ์์ด๋ค์๊ฒ ๋ฌด์ ํ์ผ๋ก ์๋ ค์ค ์ ์์ง๋ง
// ํ์๋ค์ด ๊ณผ๋ถํ์ ๊ฑธ๋ ค์ ์์ ๋์ธ ์ ์์
/*
ํ์A๊ฐ ๋ฐฐ์ด ํ์
ํ์A๊ฐ ๋ฐฐ์ด ์ํ
ํ์A๊ฐ ๋ฐฐ์ด ์์ด
ํ์B๊ฐ ๋ฐฐ์ด ํ์
ํ์B๊ฐ ๋ฐฐ์ด ์ํ
ํ์B๊ฐ ๋ฐฐ์ด ์์ด
*/
Swift
๋ณต์ฌ
AsyncSubject
completed๋ ๋๊น์ง ์๋ฌด๋ฐ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ํ์ง ์๋ค๊ฐ, completed๋๋ฉด ๋ง์ง๋ง ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๊ณ ์ข
๋ฃ
AsyncSubject๋ โต completed๋๋ ์๊ฐ ๋ง์ง๋ง ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ๊ณ ์ข
๋ฃ๋ฉ๋๋ค. ๋ง์ฝ, ์ต๊ทผ ์ ๋ฌ๋ next ์ด๋ฒคํธ๊ฐ ์๋ค๋ฉด ๊ทธ๋ฅ completed ์ด๋ฒคํธ๋ง ์ ๋ฌํ๊ณ ์ข
๋ฃ๋๊ฒ ๋ฉ๋๋ค.
completed ์ด๋ฒคํธ ๋์ error ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ ๊ฒฝ์ฐ ์ต๊ทผ next ์ด๋ฒคํธ๊ฐ ์ ๋ฌ๋์ง ์๊ณ , error ์ด๋ฒคํธ๋ง ์ ๋ฌ๋๊ณ ์ข
๋ฃ๋ฉ๋๋ค.
let ๊ตฌ๋ชฌ์ ์๋ = AsyncSubject<String>()
let disposeBag = DisposeBag()
๊ตฌ๋ชฌ์ ์๋.onNext("ํ์")
๊ตฌ๋ชฌ์ ์๋.onNext("์ํ")
๊ตฌ๋ชฌ์ ์๋.onNext("์์ด")
let ํ์A์๋ = ๊ตฌ๋ชฌ์ ์๋
.subscribe(onNext: { (subject) in
print("ํ์A๊ฐ ๋ฐฐ์ด ",subject)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์A ์ด์ ๊ณต๋ถ ๋")
})
.disposed(by: disposeBag)
let ํ์B์๋ = ๊ตฌ๋ชฌ์ ์๋
.subscribe(onNext: { (subject) in
print("ํ์B๊ฐ ๋ฐฐ์ด ",subject)
}, onError: { (error) in
print("Erorr: ",error)
}, onCompleted: {
print("ํ์B ์ด์ ๊ณต๋ถ ๋")
})
.disposed(by: disposeBag)
๊ตฌ๋ชฌ์ ์๋.onNext("๊ตฌ๋ชฌ์ ์๋์ ๋ง์ง ๋ฆฌ์คํธ")
๊ตฌ๋ชฌ์ ์๋.onCompleted() // ๊ตฌ๋ชฌ์ ์๋ ํด๊ทผ
/*
ํ์A๊ฐ ๋ฐฐ์ด ๊ตฌ๋ชฌ์ ์๋์ ๋ง์ง ๋ฆฌ์คํธ
ํ์B๊ฐ ๋ฐฐ์ด ๊ตฌ๋ชฌ์ ์๋์ ๋ง์ง ๋ฆฌ์คํธ
ํ์A ์ด์ ๊ณต๋ถ ๋
ํ์B ์ด์ ๊ณต๋ถ ๋
*/
Swift
๋ณต์ฌ
ย Relay
Relay๋ Subject์ ์ ์ฌํ ํน์ง์ ๊ฐ์ง๊ณ ์๋ ํ์
์
๋๋ค. ์ค์ ๋ก Relay ํ์
๋ด๋ถ์์ Subject๋ฅผ ๋ํํ๊ณ ์์ต๋๋ค.
public final class PublishRelay<Element>: ObservableType {
private let subject: PublishSubject<Element>
...
}
Swift
๋ณต์ฌ
๊ทธ๋ ๋ค๋ฉด, Relay์ Subject์ ์ฐจ์ด๋ ๋ญ๊น์?
์ผ๋จ, ๊ฐ์ฅ ๋ช
ํํ ์ฐจ์ด๋ Subject๋ RxSwift์์, Relay๋ RxCocoa์์ ์ ๊ณตํด์ฃผ๋ ํ์
์
๋๋ค.
์ Relay๋ RxCocoa์์ ์ ๊ณตํด์ค๊น์?
Relay๋ Subject์ ์ ์ฌํ์ง๋ง, error, completed ์ด๋ฒคํธ๋ฅผ ๋ฐํํ์ง ์์ต๋๋ค.
์ฆ, ์๋ฌ๊ฐ ๋๋ error ์ด๋ฒคํธ๋ก ์ธํด Stream์ด ์ข
๋ฃ๋์ง ์๊ธฐ ๋๋ฌธ์ UI๋ฅผ ๊ทธ๋ฆฌ๋๋ฐ ์ ํฉํฉ๋๋ค.
Relay๋ disposed ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๊ธฐ ์ ๊น์ง ๊ณ์ ๋์ํฉ๋๋ค.
Subject์์๋ next, completed, error ์ด๋ฒคํธ๋ฅผ ๋ฐํํ์ง๋ง, Relay๋ completed, error๊ฐ ์๊ธฐ ๋๋ฌธ์ accept()๋ผ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํด์ onNext์ญํ ๋์ ํฉ๋๋ค.
ย Relay์ ์ข ๋ฅ
PublishRelay
PublishSubject๋ฅผ ๋ํํ Relay
ํด๋น ํ์
์ PublishSubject์ ํน์ฑ์ฒ๋ผ ๊ตฌ๋
์ดํ์ ๋ฐ์ํ๋ ์ด๋ฒคํธ๋ค๋ง ์ ์ ์์ต๋๋ค.
๋ํ, ๋์ผํ๊ฒ ์ด๊ธฐ๊ฐ์ด ์์ต๋๋ค.
let publishRelay = PublishRelay<Int>()
publishRelay.subscribe { print($0) }
publishRelay.accept(5)
/*
next(5)
*/
Swift
๋ณต์ฌ
BehaviorRelay
BehaviorSubject๋ฅผ ๋ํํ Relay
์์ฑ ์ ํ๋์ default ๊ฐ์ ์ ์ฅํด์ผ ํ๋ค๋ ์ ์ด BehaviorSubject์ ๊ฐ์ต๋๋ค. ๋ํ, .value๋ฅผ ํตํด์ ๋ง์ง๋ง ์ด๋ฒคํธ ๊ฐ์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
let behaviorRelay = BehaviorRelay(value: 5)
behaviorRelay.accept(6)
behaviorRelay.subscribe { print($0) }
behaviorRelay.accept(4)
print(behaviorRelay.value)
/*
next(6)
next(4)
4
*/
Swift
๋ณต์ฌ