PublishSubject

๐Ÿ“ข PublishSubject โ€” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ด ๋˜๋Š” Hot Subject

ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ: ๊ตฌ๋… ์ดํ›„ ์ด๋ฒคํŠธ๋งŒ ์ „๋‹ฌ ยท ์ดˆ๊ธฐ๊ฐ’ ์—†์Œ ยท Completed/Error ํ›„ ์ž์ฒด ์†Œ๋ฉธ

PublishSubject<Element>๋Š” Cold Observable๊ณผ Observer ์–‘์ชฝ ์—ญํ• ์„ ๋ชจ๋‘ ์ˆ˜ํ–‰ํ•˜๋Š” Hot ์ŠคํŠธ๋ฆผ์ž…๋‹ˆ๋‹ค. ๊ตฌ๋… ์‹œ์  ์ด์ „ ์ด๋ฒคํŠธ๋Š” ์ „๋‹ฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์•Œ๋ฆผ(Notification) ํŒจํ„ด์— ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.


1๏ธโƒฃ ํŠน์ง• ํ•œ๋ˆˆ์— ๋ณด๊ธฐ

ํ•ญ๋ชฉ
์„ค๋ช…
๋ฉ”๋ชจ

์ดˆ๊ธฐ๊ฐ’

โŒ ์—†์Œ

ํ•„์š”ํ•˜๋ฉด BehaviorSubject ๊ณ ๋ ค

์บ์‹œ

โŒ ์ „๋‹ฌ๋œ ๊ฐ’ ์ €์žฅ X

ReplaySubject ์ฐธ์กฐ

Hot/Cold

Hot

๊ตฌ๋… ์ด์ „ ์ด๋ฒคํŠธ ์†์‹ค ๊ฐ€๋Šฅ

์™„๋ฃŒ ์ด๋ฒคํŠธ

.onCompleted() / .onError() ๋ฐœ์ƒ ์‹œ ์ŠคํŠธ๋ฆผ ์ข…๋ฃŒ & ๋ชจ๋“  ๊ตฌ๋…์ž์— ์ „ํŒŒ

๋ฉ”๋ชจ๋ฆฌ

์™„๋ฃŒ ํ›„ ๋‚ด๋ถ€ ๋ฒ„ํผ ํ•ด์ œ

์ฃผ์ž… ๊ฐ’ ๋ˆ„์ˆ˜ ์œ„ํ—˜ ๋‚ฎ์Œ


2๏ธโƒฃ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

let subject = PublishSubject<String>()
let bag = DisposeBag()

// 1st ๊ตฌ๋…์ž
subject
    .subscribe(onNext: { print("๐Ÿ…ฐ๏ธ:",$0) })
    .disposed(by: bag)

subject.onNext("Hello")

// 2nd ๊ตฌ๋…์ž (์ด ์‹œ์  ์ด์ „ ๊ฐ’ ๋ฐ›์ง€ ๋ชปํ•จ)
subject
    .subscribe(onNext: { print("๐Ÿ…ฑ๏ธ:",$0) })
    .disposed(by: bag)

subject.onNext("RxSwift")
subject.onCompleted()

์ถœ๋ ฅ

๐Ÿ…ฐ๏ธ: Hello
๐Ÿ…ฐ๏ธ: RxSwift
๐Ÿ…ฑ๏ธ: RxSwift
  • ๋‘ ๋ฒˆ์งธ ๊ตฌ๋…์ž๋Š” "Hello"๋ฅผ ๋ฐ›์ง€ ๋ชปํ•จ (Hot ํŠน์„ฑ).

  • onCompleted() ์ดํ›„์—” ์ƒˆ๋กœ์šด ์ด๋ฒคํŠธ๊ฐ€ ์ „๋‹ฌ๋˜์ง€ ์•Š์œผ๋ฉฐ, ์ถ”๊ฐ€ ๊ตฌ๋… ์‹œ .completed ์ฆ‰์‹œ ์ˆ˜์‹ .


3๏ธโƒฃ UI & ViewModel ํŒจํ„ด ์˜ˆ์ œ โ€” ๋ฒ„ํŠผ ํƒญ ์ด๋ฒคํŠธ ๋ธŒ๋กœ๋“œ์บ์ŠคํŒ…

final class MenuViewModel {
    // ์™ธ๋ถ€์— ๋…ธ์ถœํ•  readonly Observable
    let itemSelected: Observable<Int>

    private let itemSelectedSubject = PublishSubject<Int>()
    private let bag = DisposeBag()

    init(input: (buttonTap: Observable<Int>)) {
        self.itemSelected = itemSelectedSubject.asObservable()

        input.buttonTap
            .bind(to: itemSelectedSubject) // View โ–ถ๏ธŽ ViewModel
            .disposed(by: bag)
    }
}
  • ์—ฌ๋Ÿฌ ๊ตฌ๋…์ž(๋„ค๋น„๊ฒŒ์ด์…˜, ๋กœ๊ทธ ๋ชจ๋“ˆ ๋“ฑ)๊ฐ€ itemSelected๋ฅผ ๋…๋ฆฝ์ ์œผ๋กœ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ.

  • ์ด์ „ ๋ฒ„ํŠผ ํƒญ ์ด๋ฒคํŠธ๋ฅผ ์žฌ์ „์†กํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ PublishSubject๊ฐ€ ์ ํ•ฉ.


4๏ธโƒฃ ๋งˆ๋ธ” ๋‹ค์ด์–ด๊ทธ๋žจ ๋น„๊ต

Subject
๊ตฌ๋… ์ด์ „ ๊ฐ’
์ดˆ๊ธฐ๊ฐ’
์บ์‹œ ํฌ๊ธฐ

PublishSubject

โŒ

โŒ

0

BehaviorSubject

โญ• (์ตœ์‹  1๊ฐœ)

โญ•

1

ReplaySubject(3)

โญ• (์ตœ๊ทผ 3๊ฐœ)

โŒ

n

AsyncSubject

โŒ (์™„๋ฃŒ ์‹œ 1๊ฐœ)

โŒ

1 (์™„๋ฃŒ ์‹œ์ )


5๏ธโƒฃ ๋ฉ”๋ชจ๋ฆฌ & ์“ฐ๋ ˆ๋“œ ์ฃผ์˜์‚ฌํ•ญ

  1. retain cycle: subject.onNext(self) ๊ฐ™์€ self ์บก์ฒ˜ ์ฃผ์˜ โ†’ [weak self] ํŒจํ„ด ๊ถŒ์žฅ.

  2. ์™„๋ฃŒ or ์—๋Ÿฌ ํ›„ ํ•ด์ œ: ํ•„์š” ์—†๋‹ค๋ฉด subject.onCompleted() ํ˜ธ์ถœํ•ด ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ ์‹œ์  ๋ช…์‹œ.

  3. MainScheduler ๋ณด์žฅ: UI ๊ฐ’์„ ์ „๋‹ฌํ•  ๋• observe(on: MainScheduler.instance) ์„ ํ–‰.


6๏ธโƒฃ ์ž์ฃผ ํ•˜๋Š” ์‹ค์ˆ˜ & ํ•ด๊ฒฐ์ฑ…

์‹ค์ˆ˜
์ฆ์ƒ
ํ•ด๊ฒฐ

๊ตฌ๋… ์ „์— ์ด๋ฒคํŠธ ๋ฐœํ–‰

์ดˆ๊ธฐ ๊ตฌ๋…์ž๊ฐ€ ๊ฐ’ ๋ชป ๋ฐ›์Œ

๊ตฌ๋… โ†’ ์ด๋ฒคํŠธ ์ˆœ์„œ ํ™•์ธ ๋˜๋Š” BehaviorSubject ์‚ฌ์šฉ

onError ๋‚จ๋ฐœ

๋ชจ๋“  ๊ตฌ๋… ์ข…๋ฃŒ, ์•ฑ ์ƒํƒœ ๋ถˆ์•ˆ์ •

catchError๋กœ ์—๋Ÿฌ ๋ณ€ํ™˜ ํ›„ .onNext(Result.failure) ํŒจํ„ด

DisposeBag ๋ฒ”์œ„ ์˜ค๋ฅ˜

์ŠคํŠธ๋ฆผ ๊ณ„์† ์‚ด์•„์žˆ์Œ

Bag์„ VCยทVM ํ”„๋กœํผํ‹ฐ๋กœ ์„ ์–ธ, deinit ๋กœ๊ทธ ํ™•์ธ


7๏ธโƒฃ Mini Quiz

  1. PublishSubject์— ๊ตฌ๋…์ž๊ฐ€ ์—†์„ ๋•Œ onNext๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์–ด๋–ป๊ฒŒ ๋ ๊นŒ?

  2. .onError ํ˜ธ์ถœ ๋’ค onNext๋ฅผ ๋‹ค์‹œ ๋ณด๋‚ด๋ฉด?

  3. ๋™์ผ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์‹œ PassthroughSubject(Combine)์™€ ๋‹ค๋ฅธ ์  ํ•œ ๊ฐ€์ง€?

์ •๋‹ต
  1. ๊ตฌ๋…์ž ์—†์Œ โ†’ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐ”๋กœ ํ๊ธฐ๋˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋Š” ์—†๋‹ค.

  2. onError๋กœ ์ŠคํŠธ๋ฆผ์ด ์ข…๋ฃŒ๋˜์–ด ๋ฌด์‹œ๋œ๋‹ค(์ „๋‹ฌ๋˜์ง€ ์•Š์Œ).

  3. Combine์˜ PassthroughSubject๋Š” completion ๊ฐ•์ œ ํƒ€์ž…๋งค๊ฐœ๋ณ€์ˆ˜ ์—†์Œ Failure == Never ์„ค์ • ๊ฐ€๋Šฅ, RxSwift๋Š” Error๊ฐ€ ๊ณ ์ • ํƒ€์ž….


๋‹ค์Œ ๋ฌธ์„œ โ–ถ๏ธ BehaviorSubject ๋กœ ์ด๋™ํ•˜์—ฌ ์ตœ์‹  ๊ฐ’ ๋ณด์กด ์ „๋žต์„ ํ•™์Šตํ•ด ๋ณด์„ธ์š”. ๐Ÿš€

Last updated