[RxSwift] ์ด๊ฒŒ ๋งž๋‚˜? ์‹ถ์€ RxSwift ๊ฐœ๋… ์ •๋ฆฌ
iOS/Swift

[RxSwift] ์ด๊ฒŒ ๋งž๋‚˜? ์‹ถ์€ RxSwift ๊ฐœ๋… ์ •๋ฆฌ

๊ณฐํŠ€๊น€ ๋‹˜์˜ RxSwift 4์‹œ๊ฐ„์— ๋๋‚ด๊ธฐ (์ข…ํ•ฉํŽธ) ์˜์ƒ์„ ๋ณด๊ณ  ์ž‘์„ฑํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค ^~^

https://youtu.be/w5Qmie-GbiA

 

์ •๋ง ์ƒ์ดˆ๋ณด,, ์•„๋ฌด๊ฒƒ๋„ ๋ชจ๋ฅด๋Š” ์ž…์žฅ์—์„œ ๊ฐ•์˜ ์˜์ƒ์„ ๋ณด๊ณ  ์˜์‹์˜ ํ๋ฆ„๋Œ€๋กœ ์ž‘์„ฑํ•œ ๊ธ€์ด๋‹ˆ ๋น„ํŒ์˜ ๋ˆˆ์œผ๋กœ ๊ธ€์„ ์ฝ์–ด ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค~~ ์˜ค๋ฅ˜ ์ง€์  ๋Œ€ํ™˜์˜!

 

[1๊ต์‹œ] ๊ฐœ๋…์žก๊ธฐ - RxSwift๋ฅผ ์‚ฌ์šฉํ•œ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

โœˆ๏ธ Rx์˜ ๋ชฉ์ 

๋น„๋™๊ธฐ์ ์œผ๋กœ ๋งŒ๋“ค์–ด์ง€๋Š” ๋ฐ์ดํ„ฐ(์–ธ์ œ ๋งŒ๋“ค์–ด์งˆ์ง€ ๋ชจ๋ฆ„)๋ฅผ completion์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋ฆฌํ„ด ๊ฐ’์œผ๋กœ ์ „๋‹ฌํ•˜์—ฌ ์ข€ ๋” ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋„๋ก ๋งŒ๋“ค์–ด์ง„ ์œ ํ‹ธ๋ฆฌํ‹ฐ

  • ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋Š” ์ž‘์—… ๋„์ค‘ ์ทจ์†Œ ์‹œ ์œ ์šฉ
  • ๋™๊ธฐ/๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ์‰ฝ๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ

create, subscribe, disposable

// MARK: RxSwift ์›ํ˜• ์‚ฌ์šฉ
    func showImageRx(_ url: String) -> Observable<UIImage?>{
        return Observable.create { seal in
            asyncLoadImage(from: url) { image in
                **seal.onNext(image) // 2
                seal.onCompleted() // 4**
            }
            return Disposables.create()
        }
    }

@IBAction func tapDownloadRxBtn(_ sender: Any) {
        disposable = showImageRx(imageURLStr)
            .observe(on: MainScheduler.instance) // 1 5
            **.subscribe**({ result in
                switch result {
                case let .next(image): // 3
                    print("called .next")
                    self.imageView.image = image
                case let .error(err):
                    print(err.localizedDescription)
                case .completed: // 6
                    print("called .completed")
                    break
                }
            })
    }
@IBAction func tapDisposeBtn(_ sender: Any) {
        disposable?.dispose()
    }

์š”๋Ÿฐ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด์„œ tapDownloadRxBtn์„ ์‹คํ–‰์‹œ์ผฐ์„ ๋•Œ! ๋กœ๊ทธ์— ์ด๋ ‡๊ฒŒ ์ฐํžˆ๋Š” ๊ฑธ ๋ณผ ์ˆ˜ ์žˆ์Œ 

์ œ ์ƒ๊ฐ์—”.. ์ฃผ์„ ๋‹ฌ์•„ ๋†“์€ ์ˆœ์„œ๋Œ€๋กœ ์ฒ˜๋ฆฌ๋˜๋Š” ๊ฒƒ ๊ฐ™์Šด๋‹ค..

 

Main ์Šค๋ ˆ๋“œ์—์„œ ๋Œ์•„๊ฐ€๋Š”(UI ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ), Observable ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” showImageRx() ๋ฅผ ๋งŒ๋“ค์–ด ๋‘๊ณ , asyncLoadImage()๋ฅผ ํ†ตํ•ด ๋งŒ๋“ค์–ด์ง„ image๋ฅผ onNext โ†’ onCompleted ์ˆœ์„œ๋กœ ์‹คํ–‰์‹œํ‚ด.

 

onNext โ†’ switch-case ๋ฌธ์˜ .next๋กœ ๋น ์ ธ ๊ตฌ๋ฌธ์„ ์‹คํ–‰ํ•˜๊ณ , ์™„๋ฃŒ๋˜๋ฉด ๋‹ค์Œ ์ž‘์—…์ธ onCompleted โ†’ .completed๋กœ ๋น ์ ธ ๊ตฌ๋ฌธ์„ ์‹คํ–‰ํ•จ.

 

 

๐Ÿ‘ฃ Subscribe

  • ๋‚˜์ค‘์— ์˜ค๋Š” ๋ฐ์ดํ„ฐ ๋ฐ›์œผ๋ ค๊ณ  ๋Œ€๊ธฐ
  • .subscribe์˜ result์˜ ๊ฒฝ์šฐ์˜ ์ˆ˜๋Š” switch-case ๋ฌธ์ฒ˜๋Ÿผ ์žˆ์œผ๋ฉฐ, ๋‹ค๋ฅธ ๊ฒฝ์šฐ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ๊ฒ ์Œ. ์ด๋“ค์€ ๊ตฌ๋…๊ณผ์•Œ๋ฆผ์„ค์ •์ฒ˜๋Ÿผ... result๋กœ ๋“ค์–ด์˜ค๋Š” ์นœ๊ตฌ๋“ค์— ๋Œ€ํ•ด ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ ค๊ณ  ํ•ญ์ƒ ๊ตฌ๋…ON ์ƒํƒœ์ธ ๊ฒŒ ์•„๋‹๊นŒ...??????

 

๐ŸŒ€ Dispose โ†’ ์ทจ์†Œ

// ์„ ์–ธ
var disposable: Disposable?

// dispose
disposable?.dispose()
  • DisposeBag(๊ฐ€๋ฐฉ): Dispose๋“ค์„ ๋‹ด๋Š” ๊ณณ, ์—ฌ๋Ÿฌ๊ฐœ์˜ Dispose๋“ค์„ ๋‹ด์•„์„œ ํ•œ๋ฒˆ์— ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
// ์„ ์–ธ
var disposeBag = DisposeBag()

// dispose
Observable.just("Hello World")
            .subscribe(onNext: { str in
                print(str)
            })
            **.disposed(by: disposeBag)**

// ๋ชจ๋“  Observable ์ดˆ๊ธฐํ™”
disposeBag = DisposeBag()
  • Dispose๋ฅผ ํ•ด์•ผ ํ•˜๋Š” ์ด์œ  - ์•ผ๊ณฐ๋‹ท๋„ท ๊ฒŒ์‹œ๊ธ€์—์„œ ๋ฐœ์ทŒ

 

๐Ÿ‘€ Observable ๋‹ค์–‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๊ธฐ

์ •์˜๋ฅผ ๋ญ๋ผ๊ณ  ๋‚ด๋ฆฌ๋ฉด ์ข‹์„๊นŒ...

Observable.just("800x600")
            .map { $0.replacingOccurrences(of: "x", with: "/") }
            .map { "https://picsum.photos/\($0)/?random" }
            .map { URL(string: $0) }
            .filter { $0 != nil }
            .map { $0! }
            .map { try Data(contentsOf: $0) }
            .map { UIImage(data: $0) }
            .subscribe(onNext: { image in
                self.imageView.image = image
            })
            .disposed(by: disposeBag)

.just (์ƒ์„ฑ)

  • ๋ฐ์ดํ„ฐ ํ•˜๋‚˜๋งŒ ๋ฐ›์Œ?!?

.from (์ƒ์„ฑ)

  • ๋ฐฐ์—ด๋งŒ ๋ฐ›์Œ! ๋ฐฐ์—ด์ด ์•ž์—์„œ๋ถ€ํ„ฐ ํ•˜๋‚˜์”ฉ ๋“ค์–ด๊ฐ (์š”์†Œ ๋ฐ์ดํ„ฐํƒ€์ž… ์ƒ๊ด€ ใ„ดใ„ด)
  • .from(["RxSwift", "In", 4, "Hours"])

.map (๋ณ€ํ˜•)

  • ๋ฌด์–ธ๊ฐ€ ๋ฐ›์€ ๊ฐ’์ด ์žˆ์„ ๋•Œ! ๊ฑ”๋ฅผ { ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•จ }
  • .map { str in "\(str) RxSwift" }
  • from ๋“ฑ ์ƒ์„ฑํ•˜๋Š” ์นœ๊ตฌ๋“ค๊ณผ ๊ฐ™์ด ์“ธ ์ˆ˜ ์žˆ์Œ!
    • ์ด๋ ‡๊ฒŒ ์ค„ ์„ธ์›Œ์„œ from ๊ฑฐ์ณค๋‹ค๊ฐ€ map ๊ฑฐ์ณค๋‹ค๊ฐ€ subscribe๊นŒ์ง€... ์ฃผ๋ฅด๋ฅต ์ด์–ด์ง€๋Š” ๊ฒƒ์„ Stream์ด๋ผ๊ณ  ํ•จ!
    • Observable์˜ ๋‚˜์—ด๋“ค(Stream)์œผ๋กœ ๋น„๋™๊ธฐ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ํ•˜๋Š” ๊ฒƒ์ด Rx..~~!
  • Observable.from(["with", "๊ณฐํŠ€๊น€"]) .map { $0.count } .subscribe(onNext: { str in print(str) }) // 4 3

.filter (ํ•„ํ„ฐ)

  • if๋ฌธ์ฒ˜๋Ÿผ true์ผ ๋•Œ๋งŒ ๋ฐ‘์œผ๋กœ ๋‚ด๋ ค๊ฐ€๋„๋ก(ํ†ต๊ณผํ•˜๋„๋ก) ์‚ฌ์šฉ!

 

โž• ์ถ”๊ฐ€๋กœ ์ •๋ฆฌํ•˜๋ฉด ์ข‹์„ ๋‚ด์šฉ(๊ฐœ๊ฐœ๊ฐœ๊ฐœ๊ฐœ๊ฐœ๊ฐœ๊ฐœใ…์ธ์ )

$0, $1... ์ด๋ž€? - ํ•ญ์ƒ ์ •ํ™•ํ•œ ์˜๋ฏธ๋ฅผ ๋ชจ๋ฅด๋Š” ์ฑ„๋กœ ์‚ฌ์šฉํ•ด ์™”๋Š”๋ฐ ๋‹ค์Œ ์Šคํ„ฐ๋””์—์„œ ์ด๊ฑธ ์ •๋ฆฌํ•ด ์ฃผ์‹œ๋Š” ๋ถ„์ด ๊ณ„์…”์„œ ์‹ ๋‚œ๋‹ค!