Skip to content

Latest commit

ย 

History

History
592 lines (427 loc) ยท 33.8 KB

File metadata and controls

592 lines (427 loc) ยท 33.8 KB

์ด๊ธ€์€ Getting Started With RxSwift and RxCocoa ( https://www.raywenderlich.com/138547/getting-started-with-rxswift-and-rxcocoa )์— ๋Œ€ํ•œ ๋ฒˆ์—ญ์ž…๋‹ˆ๋‹ค. (๋ฒˆ์—ญ ํ—ˆ๋ฝ์„ ๋ฐ›์€ ๊ฒƒ์€ ์•„๋‹ˆ์ง€๋งŒ, ๋„์›€์ด ํ•„์š”ํ•˜์‹  ๋ถ„๋“ค์„ ์œ„ํ•ด์„œ ์˜ฌ๋ ค๋ด…๋‹ˆ๋‹ค. ๋ฌธ์ œ๊ฐ€ ๋  ๊ฒฝ์šฐ ๋ฐ”๋กœ ์‚ญ์ œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ใ…กใ…œ)

RxSwift ์™€ RxCocoa ์‹œ์ž‘ํ•˜๊ธฐ

์ฝ”๋“œ๊ฐ€ ์—ฌ๋Ÿฌ๋ถ„์ด ์›ํ•˜๋Š” ๋Œ€๋กœ ์ •ํ™•ํžˆ ๋™์ž‘ํ•˜๋ฉด(์ €์˜ ๊ณ ์–‘์ด์™€๋Š” ๋‹ฌ๋ฆฌ) ๋งค์šฐ ๊ธฐ์ฉ๋‹ˆ๋‹ค. ํ”„๋กœ๊ทธ๋žจ์—์„œ ์–ด๋–ค ๊ฒƒ์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์—…๋ฐ์ดํŠธ ํ•˜๋„๋ก ์ด์•ผ๊ธฐํ•˜๋ฉด ๊ทธ๋Œ€๋กœ ๋ฉ๋‹ˆ๋‹ค. ์ข‹์€ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

๊ฐ์ฒด์ง€ํ–ฅ ์‹œ๋Œ€์— ๋Œ€๋ถ€๋ถ„์˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ๋ช…๋ นํ˜•(imperative)์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฐ์‹์ด์—ˆ์ฃ . ์ฝ”๋“œ๊ฐ€ ํ”„๋กœ๊ทธ๋žจ์—๊ฒŒ ํ•ด์•ผํ•  ์ผ์„ ๋งํ•˜๊ณ , ๋ณ€๊ฒฝ์‚ฌํ•ญ์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ณดํ†ต ๋ญ”๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์‹œ์Šคํ…œ์— ์ ๊ทน์ ์œผ๋กœ ์•Œ๋ ค์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ๋Œ€๋กœ ๊ดœ์ฐฎ์•˜์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ๋งŒ์•ฝ ์—ฌ๋Ÿฌ๋ถ„์ด ์•ฑ์—์„œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐœ์ƒํ•  ๋•Œ ์ž๋™์œผ๋กœ ์ฝ”๋“œ๊ฐ€ ์—…๋ฐ์ดํŠธํ•˜๋„๋ก ์„ค์ •ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค๋ฉด ๊ทธ๊ฒƒ๋ณด๋‹ค ์ข‹์„ ์ˆ˜ ์žˆ์„๊นŒ์š”? ์ด๊ฒƒ์ด reactive programming ์˜ ๊ธฐ๋ณธ ์‚ฌ์ƒ์ž…๋‹ˆ๋‹ค.: ์•ฑ์ด ์ง์ ‘์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ํ•˜๋ผ๋Š” ์—ฌ๋Ÿฌ๋ถ„์˜ ์ง€์‹œ ์—†์ด ๋ฐ์ดํ„ฐ์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ์— ๋ฐ˜์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ํŠน์ • ์ƒํƒœ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋ฐ”๋กœ ์•ž์˜ ๋กœ์ง์— ์ง‘์ค‘ํ•˜๊ธฐ ๋” ์‰ฌ์›Œ์ง‘๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๊ฒƒ์€ ์ˆœ์ˆ˜ Objective-C ์—์„œ๋Š” ๋ณดํ†ต Key-Value Observation ( https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html ) ์œผ๋กœ ํ•  ์ˆ˜ ์žˆ๊ณ , Swift์—์„œ๋Š” didSet ํ˜น์€ setter๋ฅผ override ํ•ด์„œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋•Œ๋•Œ๋กœ ์ด๋Ÿฐ ๋ฉ”์†Œ๋“œ๋“ค์€ ์ œ๋Œ€๋กœ ๋‹ค๋ฃจ๊ธฐ๊ฐ€ ํž˜๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Objective-C ์™€ Swift ์—๋Š” ์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ”ผํ•ด reactive programming ์„ ๋” ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” ์—ฌ๋Ÿฌ framework๋“ค์ด ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.

์ฃผ์˜: ์ข€ ๋” ์ž์„ธํžˆ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด, Rui Peres๊ฐ€ ์ž‘์„ฑํ•œ ์ฃผ์š” framework ๋“ค๊ฐ„์˜ ์ฐจ์ด์ ์„ ์„ค๋ช…ํ•œ ์ข‹์€ ๋ฌธ์„œ( https://www.raywenderlich.com/126522/reactivecocoa-vs-rxswift )๋ฅผ ๋ณด์„ธ์š”. ๋Œ“๊ธ€ ์˜์—ญ์— ์—ด์ •์ ์ธ ์‚ฌ๋žŒ๋“ค์ด ์–ด๋–ค framework์ด ๋” ์ข‹๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ง€์— ๋Œ€ํ•œ ์žฌ๋ฏธ์žˆ๋Š” ๊ด€์  ๋˜ํ•œ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ค๋Š˜ ์—ฌ๋Ÿฌ๋ฒˆ์€ ์ดˆ์ฝœ๋ฆฟ ๊ตฌ๋งค ์•ฑ์„ ์„ฑ๊ฐ€์‹  ๋ช…๋ นํ˜•(imperative)์—์„œ ๋ฉ‹์ง„ ๋ฐ˜์‘ํ˜•(reactive)์œผ๋กœ ํƒˆ๋ฐ”๊ฟˆ์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ ์ด๋Ÿฐ framework๋“ค ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค. RxSwift, ๊ทธ๋ฆฌ๊ณ  ๊ทธ์˜ ๋™๋ฃŒ RxCocoa๋ฅผ ์‚ฌ์šฉํ•˜๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค.

RxSwift ์™€ RxCocoa๊ฐ€ ๋ญ”๊ฐ€์š”?

RxSwift ์™€ RxCocoa๋Š” ReactiveX(๋ณดํ†ต Rx๋ผ๊ณ  ์ถ•์•ฝ๋ฉ๋‹ˆ๋‹ค.) ์–ธ์–ด ๋„๊ตฌ๋“ค ์˜ suite ์˜ ์ผ๋ถ€์ž…๋‹ˆ๋‹ค. ReactiveX๋Š” ์—ฌ๋Ÿฌ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์™€ ํ”Œ๋žซํผ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ReactiveX๊ฐ€ .NET/C# ์ƒํƒœ๊ณ„์˜ ์ผ๋ถ€๋กœ์„œ ์‹œ์ž‘ํ•˜๊ธฐ๋Š” ํ–ˆ์ง€๋งŒ, ๋ฃจ๋น„ ๊ฐœ๋ฐœ์ž, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐœ๋ฐœ์ž, ํŠนํžˆ ์ž๋ฐ”์™€ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์ž๋“ค ์‚ฌ์ด์—์„œ ํญ๋ฐœ์ ์ธ ์ธ๊ธฐ๋ฅผ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค.

RxSwift๋Š” Swift ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์™€ ์ž‘๋™ํ•˜๋Š” framework์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด์— RxCocoa๋Š” iOS์™€ OSX์—์„œ ์‚ฌ์šฉ๋˜๋Š” Cocoa API๋ฅผ reactive ํ…Œํฌ๋‹‰๋“ค๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋„์›€์„ ์ฃผ๋Š” framework์ž…๋‹ˆ๋‹ค.

ReactiveX framework๋Š” ๊ณตํ†ต ์šฉ์–ด๋ฅผ ์ œ๊ณตํ•จ์œผ๋กœ์จ ๋‹ค์–‘ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ ๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” ํŠน์ • ์ž‘์—…๋“ค์— ์œ ์šฉํ•˜๊ฒŒ ์„ค๊ณ„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. (์ด๋ก ์ ์œผ๋กœ) ์ด๊ฒƒ์€ ์ƒˆ๋กœ์šด ์–ธ์–ด์— ๊ณตํ†ต ์ž‘์—…์„ ์–ด๋–ป๊ฒŒ ๋งคํ•‘ํ• ์ง€ ์•Œ์•„ ๋‚ด๋Š”๋ฐ ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•˜๊ธฐ ๋ณด๋‹ค๋Š” ์–ธ์–ด ๊ทธ์ž์ฒด์˜ ๋ฌธ๋ฒ•์— ๋” ์ง‘์ค‘ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.

Observables ์™€ Observers

์ด ํŠœํ„ฐ๋ฆฌ์–ผ์„ ์œ„ํ•ด ์•Œ์•„์•ผ ํ•  ๋‘๊ฐ€์ง€ ๊ฐœ๋…์€ Observable ๊ณผ Observer ์ž…๋‹ˆ๋‹ค.

  • Observable ๋Š” ๋ณ€๊ฒฝ ์•Œ๋ฆผ์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š”(emit) ์–ด๋–ค ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • Observer๋Š” ๋ณ€๊ฒฝ ์‚ฌํ•ญ์ด ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์•Œ๋ฆผ์„ ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ Observable์— ๋Œ€ํ•ด์„œ ๊ตฌ๋…(subscribe)ํ•˜๋Š” ์–ด๋–ค ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•˜๋‚˜์˜ Observable์— ์—ฌ๋Ÿฌ Observer๋“ค์ด listen(subscribe)ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Observable ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ๊ทธ๊ฒƒ์˜ ๋ชจ๋“  Observer๋“ค์—๊ฒŒ ์•Œ๋ฆผ์ด ์ „๋‹ฌ๋œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

DisposeBag

RxSwift ์™€ RxCocoa๋Š” ARC์™€ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ๋ฅผ ๋‹ค๋ฃฐ ์ถ”๊ฐ€์ ์ธ ํˆด์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. DisposeBag ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์„ Observer ๊ฐ์ฒด๋“ค์— ๋Œ€ํ•œ ๊ฐ€์ƒ์˜ ๊ฐ€๋ฐฉ("bag") ์ž…๋‹ˆ๋‹ค. DisposeBag์— ์žˆ๋Š” Observer๋“ค์€ ๋ถ€๋ชจ ๊ฐ์ฒด๊ฐ€ deallocte๋  ๋•Œ, ํ๊ธฐ๋ฉ๋‹ˆ๋‹ค.

DisposeBag์„ property๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฐ์ฒด์— ๋Œ€ํ•ด์„œ deinit() ์ด ํ˜ธ์ถœ๋  ๋•Œ, ๊ทธ ๊ฐ€๋ฐฉ(DisposeBag)์€ ๋น„์›Œ์ง‘๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ํ๊ธฐ๋˜๋Š” Observer๋Š” ์ž๋™์œผ๋กœ ๊ด€์ฐฐํ•˜๊ณ (observe) ์žˆ๋˜ ๊ฒƒ์œผ๋กœ ๋ถ€ํ„ฐ ํ•ด์ง€(unsubscribe)๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ๋ณดํ†ต ๊ทธ๋ ‡๋“ฏ์ด ARC๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ํ•ด์ œํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.(reference count๊ฐ€ ์ค„์–ด์„œ 0์ด ๋˜๊ณ  ARC๊ฐ€ ํ•ด์ œํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.)

DisposeBag์ด ์—†๋‹ค๋ฉด, ๋‘๊ฐ€์ง€ ์ค‘ ํ•˜๋‚˜์˜ ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค.:Observer๊ฐ€ retain cycle์„ ๋งŒ๋“ค๊ฒŒ ๋˜๊ณ , ๋ฌดํ•œ์ • ๊ด€์ฐฐํ•˜๊ฒŒ ๋˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ๋ถ„์˜ ๊ฐ์ฒด์—์„œ deallocted ๋˜์„œ, crash๋ฅผ ์œ ๋ฐœํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์ข‹์€ ARC ์‹œ๋ฏผ์ด ๋˜๊ธฐ ์œ„ํ•ด์„œ๋Š” Observable ๊ฐ์ฒด๋“ค์„ ์ƒ์„ฑํ•  ๋•Œ, DisposeBag์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์„ธ์š”. ๊ทธ๋ž˜์•ผ Observable ๊ฐ์ฒด๋“ค์ด ๊น”๋”ํ•˜๊ฒŒ ์ •๋ฆฌ๋  ๊ฒ๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•˜๊ธฐ

์ดˆ์ฝœ๋ฆฟ์„ ๋จน์œผ๋Ÿฌ ๊ฐ‘์‹œ๋‹ค! ์ด ํŠœํ„ฐ๋ฆฌ์–ผ์„ ์œ„ํ•œ starter ์•ฑ์ธ Chocotasic ์€ ์—ฌ๊ธฐ( https://koenig-media.raywenderlich.com/uploads/2016/10/Chocotastic-starter-s3-rxs-3b1.zip )์—์„œ ๋ฐ›์œผ์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
zip ํŒŒ์ผ์„ ๋‹ค์šด๋กœ๋“œ ๋ฐ›๊ณ  Xcode์—์„œ project๋ฅผ ์—ฌ์„ธ์š”.

์ฃผ์˜:์ด ํ”„๋กœ์ ํŠธ๋Š” CocoaPods๋ฅผ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Xcode์—์„œ Chocotasic.xcworkspace ํŒŒ์ผ์„ Xcode์—์„œ ์—ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์•ฑ์„ ๋นŒ๋“œํ•˜๊ณ  ์‹คํ–‰์‹œํ‚ค์„ธ์š”. ๋งˆ์นจ๋‚ด ์—ฌ๋Ÿฌ๋ถ„์€ ๋‹ค์Œ์˜ ํ™”๋ฉด์„ ๋ณด๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค. ๊ฐœ๋ณ„ ๊ฐ€๊ฒฉ๊ณผ ํ•จ๊ป˜ ์œ ๋Ÿฝ์—์„œ ์‚ด ์ˆ˜ ์žˆ๋Š” ๋ช‡๊ฐ€์ง€ ์ดˆ์ฝœ๋ฆฟ ์ข…๋ฅ˜๋“ค์ด ๋ฆฌ์ŠคํŠธ๋กœ ๋ณด์—ฌ์ง‘๋‹ˆ๋‹ค.

์ดˆ์ฝœ๋ฆฟ ์— ํƒญ์„ ํ•˜๋ฉด ์นดํŠธ์— ๊ทธ ์ œํ’ˆ์ด ์ถ”๊ฐ€ ๋  ๊ฒ๋‹ˆ๋‹ค.

์šฐ์ธก ์ƒ๋‹จ์— ์žˆ๋Š” ์นดํŠธ๋ฅผ ํƒญํ•˜๋ฉด ์ฒดํฌ ์•„์›ƒํ•˜๊ฑฐ๋‚˜ cart๋ฅผ resetํ•  ์ˆ˜ ์žˆ๋Š” ํŽ˜์ด์ง€๋กœ ์ด๋™๋ฉ๋‹ˆ๋‹ค.

์ฒดํฌ ์•„์›ƒ์„ ์„ ํƒํ•˜๋ฉด, ์‹ ์šฉ์นด๋“œ ์ž…๋ ฅ ํผ์ด ๋‚˜ํƒ€๋‚  ๊ฒ๋‹ˆ๋‹ค.

ํŠœํ„ฐ๋ฆฌ์–ผ์—์„œ ๋‚˜์ค‘์—, ์ˆœ์ˆ˜ reactive programming์„ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋‹ค์‹œ ๋Œ์•„ ์˜ฌ๊ฒ๋‹ˆ๋‹ค. cart ์š”์•ฝ ํŽ˜์ด์ง€๋กœ ๋Œ์•„๊ฐ€๋ ค๋ฉด Cart ๋ฒ„ํŠผ์„ ํƒญํ•˜์„ธ์š”. ๊ทธ๋Ÿฐ๋‹ค์Œ ๋นˆ cart ๋ฅผ ๊ฐ€์ง„ ๋ฉ”์ธ ๋ฉ”์ด์ง€๋กœ ๋Œ์•„ ๊ฐ€๋ ค๋ฉด Reset ๋ฒ„ํŠผ์„ ํƒญํ•˜์„ธ์š”.

์‹œ์ž‘ํ•˜๊ธฐ: Non-reactive

์ด์ œ๊นŒ์ง€ ์—ฌ๋Ÿฌ๋ถ„์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์–ด๋–ค์ง€ ๋ดค์Šต๋‹ˆ๋‹ค. ์ด์ œ๋Š” ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์กฐ์‚ฌํ•ด๋ณผ ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค. ChocolatesOfTheWorldViewController.swift๋ฅผ ์—ฌ์„ธ์š”. ์—ฌ๋Ÿฌ๋ถ„์€ ๊ฝค ์ „ํ˜•์ ์ธ UITableViewDelegate์™€ UITableViewDataSource๋ฅผ extension๋“ค์„ ๋ณด๊ฒŒ ๋ ๊ฒ๋‹ˆ๋‹ค.

updateCartButton() ๋ฉ”์†Œ๋“œ๊ฐ€ ๋˜ํ•œ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋Š” cart button์„ ์นด๋“œ์— ์žˆ๋Š” ์ดˆ์ฝœ๋ฆฟ์˜ ๊ฐœ์ˆ˜๋กœ ์—…๋ฐ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋Š” ๋‘ ๊ณณ์—์„œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. view controller๊ฐ€ ๋ณด์—ฌ์งˆ ๋•Œ ํ˜ธ์ถœ๋˜๋Š” viewWillAppear(:) ์™€ ์นดํŠธ์— ์ƒˆ๋กœ์šด ์ดˆ์ฝœ๋ฆฟ์ด ์ถ”๊ฐ€๋œ ํ›„์— ํ˜ธ์ถœ๋˜๋Š” tableView):didSelectRowAt:)์—์„œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ๋“ค์€ ๋‘˜๋‹ค ๋ช…๋ นํ˜•(imperative)์ธ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.:๋ช…์‹œ์ ์œผ๋กœ ๊ฐœ์ˆ˜๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ์–ด๋””์—์„œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋Š”์ง€ ๊ธฐ์–ตํ•ด๋‘ฌ์•ผ ํ•˜์ง€๋งŒ, ๋ฐ˜์‘ํ˜•(reactive) ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์žฌ์ž‘์„ฑํ•  ๊ฒ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ํ•˜๋ฉด, ๊ฐœ์ˆ˜๊ฐ€ ์–ด๋””์—์„œ ์–ด๋–ป๊ฒŒ ๋ณ€๊ฒฝ๋˜๋˜์ง€ ์ƒ๊ด€์—†์ด ๋ฒ„ํŠผ์ด ์—…๋ฐ์ดํŠธ ๋  ๊ฒ๋‹ˆ๋‹ค.

RxSwift:Cart์˜ ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜์‘ํ˜•(reactive)์œผ๋กœ ๋งŒ๋“ค๊ธฐ

์นด๋“œ์˜ item๋“ค์„ ์ฐธ์กฐํ•˜๋Š” ๋ชจ๋“  ๋ฉ”์†Œ๋“ค์€ ShoppingCart.sharedCart ์‹ฑ๊ธ€ํ†ค(SingleTon)์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ShoppingCart.swift๋ฅผ ์—ฌ์„ธ์š”. ๊ทธ๋Ÿฌ๋ฉด ์ธ์Šคํ„ด์Šค ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ๊ฝค ์ „ํ˜•์ ์ธ ์‹ฑํดํ„ด(SingleTon)์„ ๋ณด๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค.

var chocolates = [Chocolate]()

๋‹น์žฅ์€, chocolates ๋ณ€์ˆ˜์˜ ๋‚ด์šฉ์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๊ด€์ฐฐ(observe)ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ •์˜์— didSet ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ฐฐ์—ด์˜ ๊ฐ ์š”์†Œ๊ฐ€ ์•„๋‹Œ ์ „์ฒด ๋ฐฐ์—ด์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ๋งŒ ํ˜ธ์ถœ๋  ๊ฒ๋‹ˆ๋‹ค.

๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ๋„, RxSwift๋Š” ํ•ด๋‹ต์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. chocolates ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ค„์„ ์ด๋ ‡๊ฒŒ ๋ณ€๊ฒฝํ•˜์„ธ์š”.

let chocolates: Variable<[Chocolate]> = Variable([])

์ฃผ์˜: ์ด ๋ณ€๊ฒฝ์€ ์‚ฌ์ด๋“œ๋ฐ”์— ์ƒ๋‹นํ•œ ์˜ค๋ฅ˜๋“ค์„ ์œ ๋ฐœํ•  ๊ฒ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ธˆ๋ฐฉ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.

์ด ๋ฌธ๋ฒ•์€ ์กฐ๊ธˆ ์–ด๋ ค์›Œ์„œ ๋จธ๋ฆฌ๋ฅผ ๊ฐ์‹ธ๊ฒŒ ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ์ด๋ฉด์—์„œ ์–ด๋–ค ๊ฒƒ๋“ค์ด ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒƒ์„ ์ดํ•ดํ•˜๋Š”๋ฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.

chocolates ๋ฅผ Chocolate ๊ฐ์ฒด๋“ค์˜ Swift ๋ฐฐ์—ด๋กœ ์„ค์ •ํ•˜๊ธฐ ๋ณด๋‹ค๋Š”, Chocolate ๊ฐ์ฒด๋“ค์˜ Swift ๋ฐฐ์—ด์˜ ํƒ€์ž…์„ ๊ฐ€์ง€๋Š” RxSwift Variable ๋กœ ์ •์˜ํ–ˆ์Šต๋‹ˆ๋‹ค.

Variable๋Š” ํด๋ž˜์Šค์ด๊ณ , ๊ทธ๋ž˜์„œ reference ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. - chocolates ๊ฐ€ Variable ์ธ์Šคํ„ด์Šค๋ฅผ ์ฐธ์กฐํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

Variable ๋Š” value๋ผ๋Š” property๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Chocolate ๊ฐ์ฒด๋“ค์—๋Œ€ํ•œ ๋ฐฐ์—ด์ด ์ €์žฅ๋˜๋Š” ๊ณณ์ž…๋‹ˆ๋‹ค.

Variable์˜ ๋งˆ์ˆ ์€ asObservable()์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” ๋ฉ”์†Œ๋“œ๋กœ ๋ถ€ํ„ฐ ์˜ต๋‹ˆ๋‹ค. ๋งค๋ฒˆ ์ˆ˜๋™์œผ๋กœ value ๋ฅผ ํ™•์ธํ•˜๋Š” ๋Œ€์‹ ์—, ์—ฌ๋Ÿฌ๋ถ„ ๋Œ€์‹ ์— ๊ณ„์† ์ง€์ผœ๋ด์ค„ Observer๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ, Observer๊ฐ€ ์—ฌ๋Ÿฌ๋ถ„์—๊ฒŒ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ณ€๊ฒฝ์— ๋ฐ˜์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์„ค์ •์—์„œ ๋‹จ์ ์€ chocolates ๋ฐฐ์—ด์— ์–ด๋–ค ๊ฒƒ์— ์ ‘๊ทผํ•˜๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์žˆ์„ ๋•Œ, ์ง์ ‘์ ์œผ๋กœ ํ•˜์ง€ ๋ชปํ•˜๊ณ , value property๋ฅผ ํ†ตํ•ด์„œ ํ•ด์•ผ ํ•˜๋Š” ๊ฒ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์—๋Ÿฌ๋ฅผ ๋‚ด๋ฑ‰๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ์ด์ œ ๊ทธ ์˜ค๋ฅ˜๋“ค์„ ์ˆ˜์ •ํ•  ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค.!

ShoppingCart.swift์—์„œ totalCost()๋ฉ”์†Œ๋“œ๋ฅผ ์ฐพ๊ณ 

return chocolates.reduce(0) {

์—์„œ

return chocolates.value.reduce(0) {

์œผ๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.

itemCountString()์—์„œ

guard chocolates.count > 0 else {

์—์„œ

guard chocolates.value.count > 0 else {

์œผ๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.

๊ทธ๋ฆฌ๊ณ 

let setOfChocolates = Set<Chocolate>(chocolates)

์—์„œ

let setOfChocolates = Set<Chocolate>(chocolates.value)

์œผ๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.

๋งˆ์ง€๋ง‰์œผ๋กœ

let count: Int = chocolates.reduce(0) {

์—์„œ

let count: Int = chocolates.value.reduce(0) {

์œผ๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.

CartViewController.swift์—์„œ reset()์„ ์ฐพ๊ณ ,

ShoppingCart.sharedCart.chocolates = []

์—์„œ

ShoppingCart.sharedCart.chocolates.value = []

์œผ๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.

ChocolatesOfTheWorldViewController.swift ์—์„œ ๋‹ค์‹œ, updateCartButton()์˜ ๊ตฌํ˜„์„

cartButton.title = "\(ShoppingCart.sharedCart.chocolates.value.count) \u{1f36b}"

์œผ๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.

๊ทธ๋ฆฌ๊ณ  tableView(_:didSelectRowAt:)์—์„œ

ShoppingCart.sharedCart.chocolates.append(chocolate)

์—์„œ

ShoppingCart.sharedCart.chocolates.value.append(chocolate)

์œผ๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.

ํœด, ๋งˆ์นจ๋‚ด, Xcode ๊ฐ€ ํ–‰๋ณตํ•  ๊ฒ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์—๋Ÿฌ๊ฐ€ ์—†์„ ๊ฒ๋‹ˆ๋‹ค. ์ด์ œ chocolates๊ฐ€ ๊ด€์ฐฐ๋ (observed) ์ˆ˜ ์žˆ๋‹ค๋Š” ์ด์ ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

ChocolatesOfTheWorldViewController.swift ๋กœ ๊ฐ€์„ธ์š”. ๊ทธ๋ฆฌ๊ณ  property๋“ค์˜ ๋ฆฌ์ŠคํŠธ์— ๋‹ค์Œ์„ ์ถ”๊ฐ€ ํ•˜์„ธ์š”.

let disposeBag = DisposeBag()

์ด ์ฝ”๋“œ๋Š” DisposeBag์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. DisposeBag์€ deinit()๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ, ์—ฌ๋Ÿฌ๋ถ„์ด ์„ค์ •ํ•œ Observer๋“ค์ด ์ •๋ฆฌ๋˜๋„๋ก(clean up)ํ•˜๊ธฐ ์œ„ํ•œ ๊ฒ๋‹ˆ๋‹ค.

//MARK: Rx Setup ์ฃผ์„ ์•„๋ž˜์— ๋‹ค์Œ์˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ ํ•˜์„ธ์š”.

//MARK: Rx Setup

private func setupCartObserver() {
  //1
  ShoppingCart.sharedCart.chocolates.asObservable()
    .subscribe(onNext: { //2
      chocolates in
      self.cartButton.title = "\(chocolates.count) \u{1f36b}"
    })
    .addDisposableTo(disposeBag) //3
}

์ด ์ฝ”๋“œ๋Š” cart๋ฅผ ์ž๋™์œผ๋กœ ๊ฐฑ์‹ ํ•˜๊ธฐ ์œ„ํ•ด์„œ reactive Observer๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋ณด์‹œ๋‹ค ์‹œํ”ผ, RxSwift๋Š” chained function๋“ค์„ ๋งŽ์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. chained function๋“ค์€ ๊ฐ๊ฐ์˜ ํ•จ์ˆ˜๋“ค์ด ์ด์ „ ํ•จ์ˆ˜์˜ ๊ฒฐ๊ณผ๋ฅผ ์ด์šฉํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ฒฝ์šฐ์— ์–ด๋–ป๊ฒŒ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์„ค๋ช…ํ•˜๋ฉด,

1.๋จผ์ €, shopping cart์˜ chocolates ๋ณ€์ˆ˜๋ฅผ Observable๋กœ ์žก์Šต๋‹ˆ๋‹ค.

2.Observable์˜ value ์— ๋ณ€๊ฒฝ์‚ฌํ•ญ์ด ์žˆ๋Š”์ง€ ์•Œ๊ธฐ ์œ„ํ•ด์„œ ํ•ด๋‹น Observable์— ๋Œ€ํ•ด์„œ subscribe(onNext:)๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. subscribe(onNext:)๋Š” ๊ฐ’๋“ค์ด ๋ณ€๊ฒฝ๋ ๋•Œ ๋งˆ๋‹ค ์‹คํ–‰๋  ํด๋กœ์ €๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. ํด๋กœ์ €์— ์ž…๋ ฅ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” Observable์˜ ์ƒˆ๋กœ์šด ๊ฐ’์ž…๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  unsubscribeํ•˜๊ฑฐ๋‚˜ subscribe์ด dispose๋  ๋•Œ๊นŒ์ง€ ์ด๋Ÿฐ ์•Œ๋ฆผ์„ ๊ณ„์† ๋ฐ›์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋กœ ๋ถ€ํ„ฐ ๋Œ๋ ค ๋ฐ›๋Š” ๊ฒƒ์€ Disposable์— ๋ถ€ํ•ฉํ•˜๋Š” Observer์ž…๋‹ˆ๋‹ค.

3.์ด์ „ ๋‹จ๊ณ„๋กœ ๋ถ€ํ„ฐ ์–ป์€ Observer๋ฅผ ๊ตฌ๋…ํ•˜๊ณ  ์žˆ๋Š”(subscribing) ๊ฐ์ฒด๊ฐ€ deallocte๋  ๋•Œ subscribe๊ฐ€ dispose๋˜๋„๋ก disposeBag์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ๋ช…๋ นํ˜•์˜ updateCartButton() ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ฑฐํ•˜์„ธ์š”. ์ด๊ฒƒ์€ viewWillAppear(:) ์™€ tableView(:didSelectRowAt:)์— ์˜ค๋ฅ˜๋ฅผ ์œ ๋ฐœํ•  ๊ฒ๋‹ˆ๋‹ค.

์˜ค๋ฅ˜๋“ค์„ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ, ์ „์ฒด viewWillAppear(:)๋ฅผ ์ œ๊ฑฐํ•˜์„ธ์š”.(super๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ ์ด์™ธ์—๋Š” updateCartButton()๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์ „๋ถ€์ด๊ธฐ ๋•Œ๋ฌธ์—) ๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ tableView(:didSelectRowAt)์—์„œ updateCartButton ํ˜ธ์ถœ์„ ์ œ๊ฑฐํ•˜์„ธ์š”.

๋นŒ๋“œํ•˜๊ณ  ์‹คํ–‰ํ•˜์„ธ์š”. ์ดˆ์ฝœ๋ฆฟ ๋ฆฌ์ŠคํŠธ๊ฐ€ ๋ณด์ผ ๊ฒ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ cart๋ฅผ ์œ„ํ•œ ๋ฒ„ํŠผ์ด ๋‹จ์ง€ "item"์ด๋ผ๊ณ ๋งŒ ํ‘œ์‹œ๋˜๋Š” ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•„์„ธ์š”. ๊ทธ๋ฆฌ๊ณ  ์ดˆ์ฝœ๋ฆฟ๋“ค์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ํƒญ์„ ํ•ด๋„ ์•„๋ฌด์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ญ๊ฐ€ ์ž˜๋ชป๋œ ๊ฑธ๊นŒ์š”?

Rx Observer๋“ค์„ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ, ์ง€๊ธˆ์€ ๊ทธ ํ•จ์ˆ˜๋ฅผ ์‹ค์ œ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๋ถ€๋ถ„์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ Observer๋“ค์ด ์„ค์ •๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ์˜ค๋ฅ˜๋ฅผ ์ˆ˜์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ, viewDidLoad():์— ๋‹ค์Œ์˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

setupCartObserver()

์ดˆ์ฝœ๋ฆฟ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋‹ค์‹œ ๋ณด๊ธฐ ์œ„ํ•ด์„œ ์•ฑ์„ ๋นŒ๋“œํ•˜๊ณ  ์‹คํ–‰ํ•˜์„ธ์š”.

๋ช‡๊ฐœ์˜ ์ดˆ์ฝœ๋ฆฟ์— ํƒญ์„ ํ•˜์„ธ์š”.-item๋“ค์˜ ์ˆซ์ž๊ฐ€ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋ฉ๋‹ˆ๋‹ค!

์„ฑ๊ณต!๋ชจ๋“  ์ดˆ์ฝœ๋ฆฟ์„ ์นดํŠธ์— ๋‹ด์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

RxCocoa: TableView reactiveํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ

์ง€๊ธˆ ์šฐ๋ฆฌ๋Š” RxSwift๋ฅผ ์ด์šฉํ•ด์„œ ์นดํŠธ๋ฅผ reactiveํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. UITableView๋ฅผ ์—ญ์‹œ reactiveํ•˜๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด์„œ RxCocoa๋ฅผ ์‚ฌ์šฉํ•  ๊ฒ๋‹ˆ๋‹ค.

RxCocoa๋Š” ์—ฌ๋Ÿฌ ๋‹ค๋ฅธ ์œ ํ˜•์˜ UI ์š”์†Œ๋“ค์„ ์œ„ํ•œ reactive API๋“ค์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ์ด๊ฒƒ๋“ค์€ override delegate ํ˜น์€ data source ๋ฉ”์†Œ๋“œ๋“ค์—†์ด ๋ฐ”๋กœ UITableView๋ฅผ ๊ฐ™์€ ๊ฒƒ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์˜ต์…˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฐ ๊ฒƒ๋“ค์ด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ๋ณด์—ฌ์ฃผ๊ธฐ ์œ„ํ•ด์„œ, ์ „์ฒด UITableViewDataSource์™€ UITableViewDelegate extension๋“ค๊ณผ ๊ทธ๊ฒƒ๋“ค์˜ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ๋“ค์„ ์ง€์šฐ์„ธ์š”. ๋‹ค์Œ์œผ๋กœ, viewDidLoad() ์— ์žˆ๋Š” tableView.dataSource ์™€ tableView.delegate์— ๋Œ€ํ•œ ํ• ๋‹น์„ ์ง€์šฐ์„ธ์š”.

์•ฑ์„ ๋นŒ๋“œํ•˜๊ณ  ์‹คํ–‰ํ•˜์„ธ์š”. ๊ทธ๋Ÿฌ๋ฉด ์ดˆ์ฝœ๋ฆฟ๋“ค์ด ๋ชจ๋‘ ๊ฐ‘์ž๊ธฐ ์‚ฌ๋ผ์ ธ์„œ ๊ฝค ์Šฌํ”„๊ณ  ํ……๋น„์–ด ์žˆ๋Š” tableView๋ฅผ ๋ณด๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค.

์žฌ๋ฏธ ์—†๋„ค์š”. ์ดˆ์ฝœ๋ฆฟ๋“ค์„ ๋ณต์›ํ•  ์‹œ๊ฐ„์ž…๋‹ˆ๋‹ค!

๋จผ์ €, reactive tableView๋ฅผ ๊ฐ€์ง€๊ธฐ ์œ„ํ•ด์„œ, tableView๊ฐ€ ๋ฐ˜์‘ํ•  ๋ฌด์—‡์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ChocolatesOfTheWorldViewController.swift ์—์„œ europeanChocolates property๋ฅผ Observable๋กœ ๊ฐฑ์‹ ํ•˜์„ธ์š”.

let europeanChocolates = Observable.just(Chocolate.ofEurope)

just(_:) ๋ฉ”์†Œ๋“œ๋Š” Observable์˜ ๊ธฐ์ดˆ๊ฐ€ ๋˜๋Š” value ์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ์ด ์‹ค์ œ๋กœ ์—†์ง€๋งŒ, Observable value๋กœ์„œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

์ฃผ์˜: ๋•Œ๋•Œ๋กœ, just(_:)๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์€ Reactive Programming์ด ๊ณผ๋„ํ• ์ˆ˜ ์žˆ๋‹ค๋Š” ํ‘œ์‹œ์ž…๋‹ˆ๋‹ค. ๊ฒฐ๊ตญ ๊ฐ’์ด ๋ณ€๊ฒฝ ๋˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ๊ฐ’์— ๋ฐ˜์‘ํ•˜๋„๋ก ์„ค๊ณ„๋œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์Šคํ‚ฌ์„ ์™œ ์จ์•ผ ํ• ๊นŒ์š”? ์ด ์˜ˆ์ œ์—์„œ๋Š”, ๋‚˜์ค‘์— ๋ณ€๊ฒฝ๋  view cell๋“ค์˜ ๋ฐ˜์‘์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ Rx๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ฃผ์˜๊นŠ๊ฒŒ ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์€ ์ข‹์€ ์ƒ๊ฐ์ž…๋‹ˆ๋‹ค. ๋ง์น˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ๋ชจ๋“  ๋ฌธ์ œ๊ฐ€ ๋ชป์ด๋ผ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.:]

์ด์ œ europeanChocolates์„ Observable๋กœ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์„ ์ถ”๊ฐ€ ํ•˜์„ธ์š”.

private func setupCellConfiguration() {
  //1
  europeanChocolates
    .bindTo(tableView
      .rx //2
      .items(cellIdentifier: ChocolateCell.Identifier,
             cellType: ChocolateCell.self)) { // 3
        row, chocolate, cell in
        cell.configureWithChocolate(chocolate: chocolate) //4
      }
      .addDisposableTo(disposeBag) //5
}

์—ฌ๊ธฐ์„œ ์ผ์–ด๋‚˜๋Š” ์ผ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

1.tableView ์— ๊ฐ๊ฐ์˜ row์— ๋Œ€ํ•ด์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•  ์ฝ”๋“œ์™€ europeanChocolates Observable์„ ์—ฐ๊ณ„์‹œํ‚ค๊ธฐ ์œ„ํ•ด์„œ bindTo(_:)๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

2.ํ˜ธ์ถœํ•˜๋Š” ํด๋ž˜์Šค๊ฐ€ ๋ฌด์—‡์ด๋“  ๊ฐ„์— rx๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ RxCocoa extension๋“ค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. -์ด ๊ฒฝ์šฐ์—๋Š”, UITableView

3.Rx ๋ฉ”์†Œ๋“œ items(cellIdentifier:cellType:)์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. cell Identifier์™€ cell type ์˜ class๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ฒจ์ฃผ๋ฉด์„œ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ Rx framework๊ฐ€ ๋ณดํ†ต tableView๊ฐ€ ๊ทธ๊ฒƒ์˜ ์›๋ž˜ delegate๋“ค์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด ํ˜ธ์ถœ๋  dequeuing ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

4.๊ฐ๊ฐ์˜ ์ƒˆ๋กœ์šด item ์— ๋Œ€ํ•ด์„œ ์‹คํ–‰๋  Block์„ ๋„˜๊น๋‹ˆ๋‹ค. row, row์— ์žˆ๋Š” ์ดˆ์ฝœ๋ฆฟ ๊ทธ๋ฆฌ๊ณ  cell ์— ๋Œ€ํ•œ ์ •๋ณด๋ฅผ ๋‹ค์‹œ ์–ป๊ฒŒ ๋  ๊ฒ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ cell ์„ค์ •์„ ์ •๋ง ์‰ฝ๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

5.bindTo(_:)์— ์˜ํ•ด์„œ ๋Œ๋ ค ๋ฐ›์•˜๋˜ Disposable ์„ disposeBag ์— ์ถ”๊ฐ€ ํ•ฉ๋‹ˆ๋‹ค.

๋ณดํ†ต tableView(:numberOfRowsInSection) ๊ณผ numberOfRowsInSection(in:) ์— ์˜ํ•ด์„œ ์ƒ์„ฑ๋˜๋Š” ๊ฐ’๋“ค์€ ์ด์ œ ๊ด€์ฐฐ๋˜๋Š”(observed) data์— ๊ธฐ๋ฐ˜ํ•ด์„œ ์ž๋™์œผ๋กœ ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค. tableView(:cellForRowAt:)์€ ํด๋กœ์ €์— ์˜ํ•ด์„œ ํšจ๊ณผ์ ์œผ๋กœ ๋Œ€์ฒด๋ฉ๋‹ˆ๋‹ค.

viewDidLoad()๋กœ ๊ฐ€์„ธ์š”. ๊ทธ๋ฆฌ๊ณ  ์ƒˆ๋กœ์šด setup ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ค„์„ ์ถ”๊ฐ€ ํ•˜์„ธ์š”.

setupCellConfiguration()

์•ฑ์„ ๋นŒ๋“œํ•˜๊ณ  ์‹คํ–‰์‹œํ‚ค์„ธ์š”. ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค! ์ดˆ์ฝœ๋ฆฟ๋“ค์ด ๋Œ์•„ ์™”์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ, ๊ฐ๊ฐ์˜ ์ดˆ์ฝœ๋ฆฟ์— ํƒญ์„ ํ•  ๋•Œ, ์นด๋“œ์— ๋”ํ•ด์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด์ „์˜ Rx Method ๋“ค์—์„œ ๋ญ”๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๋‚˜์š”?

์•„๋‹™๋‹ˆ๋‹ค! tableView(_:didSelectRowAt:)๋ฅผ ์ œ๊ฑฐํ•จ์œผ๋กœ์จ, cell์— ๋Œ€ํ•œ ํƒญ๋“ค์„ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ ๊ทธ๊ฒƒ๋“ค์„ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ์ˆ˜ ์žˆ๋Š” ์–ด๋–ค ๊ฒƒ์„ ๊ฐ€์ ธ์™”์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ, RxCocoa๊ฐ€ UITableView์— ์ถ”๊ฐ€ํ•œ ๋˜ ๋‹ค๋ฅธ extension method๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋Š” modelSelected(_:)๋ผ๊ณ  ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  model ๊ฐ์ฒด๋“ค์ด ์–ธ์ œ ์„ ํƒ๋  ๋•Œ, ์ •๋ณด๋ฅผ ๋ณด๋Š”๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Observable์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๋ฉ”์†Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์„ธ์š”:

private func setupCellTapHandling() {
  tableView
    .rx
    .modelSelected(Chocolate.self) //1
    .subscribe(onNext: { //2
      chocolate in
      ShoppingCart.sharedCart.chocolates.value.append(chocolate) //3

      if let selectedRowIndexPath = self.tableView.indexPathForSelectedRow {
        self.tableView.deselectRow(at: selectedRowIndexPath, animated: true)
      } //4
    })
    .addDisposableTo(disposeBag) //5
}

ํ•œ ๋‹จ๊ณ„์”ฉ ์ด๋ ‡๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.:

1.talbleView์˜ reactive extension์˜ modelSelected(_:) ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ ์ ˆํ•œ ํƒ€์ž…์˜ item์„ ๋Œ๋ ค ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ Chocolate model์„ ๊ฐ™์ด ๋„˜๊น๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” Observable์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

2.Observable์„ ๋ฐ›์œผ๋ฉด, subscribe(onNext:)๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋ธ์ด ์„ ํƒ๋ ๋•Œ๋งˆ๋‹ค(์˜ˆ๋ฅผ ๋“ค์–ด cell์ด ํƒญ๋  ๋•Œ) ์‹ค์ƒ๋˜์–ด์•ผ ํ•˜๋Š” ํด๋กœ์ €๋ฅผ ๋’ค๋”ฐ๋ฅด๊ฒŒ ํ•ด์„œ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

3.subscribe(onNext:)์— ๋„˜๊ฒจ์ง„ ๋’ค๋”ฐ๋ฅด๋Š” ํด๋กœ์ €์•ˆ์—์„œ, ์„ ํƒ๋œ ์ดˆ์ฝœ๋ฆฟ์„ ์นดํŠธ์— ๋„ฃ์Šต๋‹ˆ๋‹ค.

4.์—ญ์‹œ ํด๋กœ์ €์•ˆ์—์„œ ํƒญ๋œ row๊ฐ€ deselect๊ฐ€ ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

5.subscribe(onNext:)๋Š” Disposable ๋ฅผ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค. disposeBag์— ๊ทธ Disposable์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ, viewDidLoad()๋กœ ๊ฐ‘๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ƒˆ๋กœ์šด ์„ค์ • ๋ฉ”์†Œ๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

setupCellTapHandling()

์•ฑ์„ ๋นŒ๋“œํ•˜๊ณ  ์‹คํ–‰ํ•˜์„ธ์š”. ์นœ์ˆ™ํ•œ ์ดˆ์ฝœ๋ฆฟ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ด์ œ๋Š” ์ดˆ์ฝœ๋ฆฟ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

RxSwfit ์™€ ์ง์ ‘ Text ์ž…๋ ฅ

RxSwift์˜ ๋‹ค๋ฅธ ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์€ ์œ ์ €์— ์˜ํ•œ ์ง์ ‘ text ์ž…๋ ฅ์„ ๋ฐ›๊ณ  ๋ฐ˜์‘ํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜• text ์ž…๋ ฅ ์ฒ˜๋ฆฌ์˜ ๋ง›๋ณด๊ธฐ๋ฅผ ์œ„ํ•ด, ์‹ ์šฉ card ์ž…๋ ฅ ํผ์— ๊ฐ„๋‹จํ•œ validation ๊ณผ card type ๊ฐ์ง€ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Non-reactive ํ”„๋กœ๊ทธ๋žจ์—์„œ๋Š” ์‹ ์šฉ card ์ž…๋ ฅ ํผ์ด UITableViewDelegate ๋ฉ”์†Œ๋“œ๋“ค์ด ๋’ค์—‰์ผœ์„œ ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. ์ข…์ข… ๊ฐ๊ฐ์˜ ํ•จ์ˆ˜๋“ค์€ ๋งŽ์€ ์–ด๋–ค textField๊ฐ€ ์ž…๋ ฅ๋˜๊ณ  ์žˆ๋Š”์ง€์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ์–ด๋–ค ๋™์ž‘๊ณผ ๋กœ์ง์ด ์ ์šฉ๋˜์–ด์•ผ ํ•˜๋Š”์ง€๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” if/else ๋ฌธ์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Reactive Programming ์€ ๊ทธ ์ฒ˜๋ฆฌ๋“ค์„ ์ข€ ๋” ์ง์ ‘์ ์œผ๋กœ ๊ฐ๊ฐ์˜ ์ž…๋ ฅ field์— ๋ฌถ์Šต๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์–ด๋–ค ๋กœ์ง๋“ค์ด ์–ด๋–ค text field์— ์ ์šฉ๋˜๋Š”์ง€๋ฅผ ๋ช…ํ™•ํžˆ ํ•˜๋ฉด์„œ ์ž…๋ ฅ field์— ๋ฌถ์Šต๋‹ˆ๋‹ค.

BillingInfoViewController.swift๋กœ ๊ฐ€์„ธ์š”. ํด๋ž˜์Šค์˜ ์ฒ˜์Œ์— ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

private let disposeBag = DisposeBag()

์ด์ „ ์ฒ˜๋Ÿผ, ์ด๊ฒƒ์€ ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค๊ฐ€ deallocte ๋  ๋•Œ Observable ๋“ค์ด ์ ์ ˆํ•˜๊ฒŒ ํ๊ธฐ๋˜๋„๋ก ํ•˜๊ธฐ ์œ„ํ•ด์„œ DisposeBag์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

์œ ์ €๊ฐ€ ์‹ ์šฉ card ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•  ๋•Œ ๋„์›€์ด ๋˜๋Š” ํ•œ๊ฐ€์ง€๋Š” known card types( https://en.wikipedia.org/wiki/Payment_card_number )์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ์œ ์ €๊ฐ€ ์ž…๋ ฅํ•˜๊ณ  ์žˆ๋Š” ์‹ ์šฉ card์˜ ์ข…๋ฅ˜๊ฐ€ ๋ฌด์—‡์ธ์ง€ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ํ•˜๊ธฐ ์œ„ํ•ด์„œ, //MARK: - Rx Setup ์ฃผ์„ ์•„๋ž˜์— ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

//MARK: - Rx Setup

private func setupCardImageDisplay() {
  cardType
    .asObservable()
    .subscribe(onNext: {
      cardType in
      self.creditCardImageView.image = cardType.image
    })
    .addDisposableTo(disposeBag)
}

๊ณง, card type ๋ณ€ํ™”์— ๊ธฐ๋ฐ˜ํ•ด์„œ ์นด๋“œ ์ด๋ฏธ์ง€๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ๊ฒ๋‹ˆ๋‹ค. Observer๋ฅผ Variable์˜ value์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. Variable์˜ value๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์‹คํ–‰๋  ํด๋กœ์ €์™€ ํ•จ๊ป˜ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚˜์„œ observer๊ฐ€ disposeBag์—์„œ ์ ์ ˆํ•˜๊ฒŒ ํ๊ธฐ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ ์žฌ๋ฏธ์žˆ๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.:Text ๋ณ€๊ฒฝ ์ฒ˜๋ฆฌ

์œ ์ €๊ฐ€ ๋น ๋ฅด๊ฒŒ ํƒ€์ดํ•‘ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋ชจ๋“  ๊ธ€์ž ํ‚ค๊ฐ€ ๋ˆŒ๋Ÿฌ์งˆ ๋•Œ๋งˆ๋‹ค validation์„ ์‹คํ–‰์‹œํ‚ค๊ณ  ์‹ถ์ง€ ์•Š์„ ๊ฒ๋‹ˆ๋‹ค. ๋งค๋ฒˆ validation ๊ฒ€์‚ฌ๋ฅผ ํ•˜๋Š” ๊ฒƒ์€ ๋น„์šฉ์ ์œผ๋กœ ๋น„์‹ธ๊ณ , ๋А๋ฆฐ UI๋ฅผ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ข‹์€ ๋ฐฉ๋ฒ•์€ validation process์— ์–ผ๋งˆ๋‚˜ ๋นจ๋ฆฌ ์‚ฌ์šฉ์ž์ž…๋ ฅ์„ ๋„ฃ์„ ๊ฒƒ์ธ์ง€ debounce ํ˜น์€ throttle ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ž…๋ ฅ๋˜๋Š” ๋ชจ๋“  ๊ธ€์ž๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ฒ˜๋ฆฌ๋˜๊ธฐ ๋ณด๋‹ค๋Š” throttle ๊ฐ„๊ฒฉ์— ์‚ฌ์šฉ์ž ์ž…๋ ฅ validation ๊ฒ€์‚ฌ๊ฐ€ ์ด๋ฃจ์–ด ์ง„๋‹ค๋Š” ๊ฒƒ์ด๋ผ๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ์•„๋ฌด๋ฆฌ ๋น ๋ฅธ ์ž…๋ ฅ๋„ ์•ฑ์„ ๋ฉˆ์ถ”๊ฒŒ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰๋˜์–ด์•ผ ํ•  ๋กœ์ง์ด ์ข…์ข… ๋งŽ๊ธฐ ๋•Œ๋ฌธ์—, throttle ํ•˜๋Š” ๊ฒƒ์€ RxSwift์˜ ํŠน๋ณ„ํžˆ ์šฐ์ˆ˜ํ•œ ์ ์ž…๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋Š” ํฌ๊ฒŒ ๋กœ์ง์ด ๋งŽ์ง€ ์•Š์ง€๋งŒ, ์ž‘์€ throttle๋ฅผ ๋งŒ๋“ค ์ถฉ๋ถ„ํžˆ ๊ฐ€์น˜๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ์„ , BillingInfoViewController์˜ property ์„ ์–ธ ์•„๋ž˜ ๋‹ค์Œ์˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ ํ•˜์„ธ์š”.

private let throttleInterval = 0.1

์ด๊ฒƒ์€ ์ดˆ๋‹จ์œ„ throttle ๊ธธ์ด์— ๋Œ€ํ•œ ์ƒ์ˆ˜๊ฐ’์ž…๋‹ˆ๋‹ค.

์ด์ œ ๋‹ค์Œ์„ ์ถ”๊ฐ€ ํ•˜์„ธ์š”.

private func setupTextChangeHandling() {
  let creditCardValid = creditCardNumberTextField
    .rx
    .text //1
    .throttle(throttleInterval, scheduler: MainScheduler.instance) //2
    .map { self.validate(cardText: $0) } //3

  creditCardValid
    .subscribe(onNext: { self.creditCardNumberTextField.valid = $0 }) //4
    .addDisposableTo(disposeBag) //5
}

์ฃผ์˜: ๋งŒ์•ฝ creditCardValid๋ฅผ ์„ค์ •ํ•  ๋•Œ "Generic parameter R could not be inferred" ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด, ํƒ€์ž…์„ ๋ช…์‹œ์ ์œผ๋กœ ์„ ์–ธํ•จ์œผ๋กœ์จ Compiler๋ฅผ ์กฐ์šฉํ•˜๊ฒŒ ๋งŒ๋“ค์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (์—๋ฅผ ๋“ค์–ด let creditCardValid: Observable). ์ด๋ก ์ ์œผ๋กœ, ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์ด๊ฒƒ์„ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ฐ€๋” ์กฐ๊ธˆ ๋„์›€์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.:]

์ด ์ฝ”๋“œ๊ฐ€ ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.:

1.text ๋Š” ๋˜ ๋”ฐ๋ฅธ RxCocoa extension์ž…๋‹ˆ๋‹ค.(์‚ฌ์šฉํ•˜๊ธฐ ์ „์— rx๋ฅผ ํ˜ธ์ถœํ•จ์œผ๋กœ์จ ์นด๋ฆฌ์ผœ์ง€๋Š” RxCocoa extension์ž…๋‹ˆ๋‹ค.) ์ด๋ฒˆ์—๋Š” UITextField์— ๋Œ€ํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ Observable value๋กœ์จ textField์˜ ๋‚ด์šฉ์„ ๋ฆฌํ„ดํ•ฉ๋‹ˆ๋‹ค.

2.์ž…๋ ฅ์„ throttleํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์œ„์—์„œ ์ •์˜ํ•œ ๊ฐ„๊ฒฉ์— ๊ธฐ๋ฐ˜ํ•œ ๋Œ€๋กœ๋งŒ ์„ค์ •ํ•œ validation์ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. sheduler ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ๋” ์ง„๋ณด๋œ ๊ฐœ๋…์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์งง์€ ๋ฒ„์ „์€ ๊ทธ๊ฒƒ์„ ์Šค๋ ˆ๋“œ๋กœ ํ•œ์ •๋ฉ๋‹ˆ๋‹ค. ๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ ๋ชจ๋‘ ์œ ์ง€ํ•˜๊ธฐ๋ฅผ ์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, MainScheduler๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

3.throttle๋œ ์ž…๋ ฅ์„ validate(cardText:)์— ์ ์šฉํ•จ์œผ๋กœ์จ throttle๋œ ์ž…๋ ฅ์„ ๋ณ€ํ˜•์‹œํ‚ต๋‹ˆ๋‹ค. validate(cardText:)๋Š” ์ด๋ฏธ ํด๋ž˜์Šค์— ์˜ํ•ด์„œ ์ œ๊ณต๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์นด๋“œ ์ž…๋ ฅ์ด ์œ ํšจํ•˜๋‹ค๋ฉด, ๊ด€์ฐฐ๋˜๋Š”(observed) boolean ์˜ ์ตœ์ข…๊ฐ’์€ true๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

4.์ž…๋ ฅ๋˜๋Š” ๊ฐ’์— ๊ธฐ๋ฐ˜ํ•œ textField ์˜ ์œ ํšจ์„ฑ์ด ์—…๋ฐ์ดํŠธ๋˜๋Š” Observable ์„ subscribe ํ•ฉ๋‹ˆ๋‹ค.

5.๊ฒฐ๊ณผ์ธ Disposable ์„ disposeBag ์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

(CVV๋กœ ์•Œ๋ ค์ง„)card security code ์™€ expiration date ์— ๋Œ€ํ•œ Observable ๊ฐ’๋“ค์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด์„œ
๋‹ค์Œ์˜ ์ฝ”๋“œ๋ฅผ setupTextChangeHandling() ์˜ ๋์— ์ถ”๊ฐ€ ํ•˜์„ธ์š”.

let expirationValid = expirationDateTextField
  .rx
  .text
  .throttle(throttleInterval, scheduler: MainScheduler.instance)
  .map { self.validate(expirationDateText: $0) }

expirationValid
  .subscribe(onNext: { self.expirationDateTextField.valid = $0 })
  .addDisposableTo(disposeBag)

let cvvValid = cvvTextField
  .rx
  .text
  .map { self.validate(cvvText: $0) }

cvvValid
  .subscribe(onNext: { self.cvvTextField.valid = $0 })
  .addDisposableTo(disposeBag)

์ด์ œ 3๊ฐœ์˜ text field๋“ค์˜ ์œ ํšจ์„ฑ์— ๋Œ€ํ•ด ์„ค์ •๋œ Observable ๊ฐ’๋“ค์„ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์„ ์ถ”๊ฐ€ํ•˜์„ธ์š”.

let everythingValid = Observable
  .combineLatest(creditCardValid, expirationValid, cvvValid) {
    $0 && $1 && $2 //All must be true
  }

everythingValid
  .bindTo(purchaseButton.rx.enabled)
  .addDisposableTo(disposeBag)

์ด ์ฝ”๋“œ๋Š” Observable์˜ combineLatest(_:) ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ๋งŒ๋“ค์—ˆ๋˜ 3๊ฐœ์˜ Observable๋“ค์„ ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  4๋ฒˆ์งธ Observable์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด Observable์€ verythingValid ์ด๋ผ๋Š” ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด Observable์€ ๋ชจ๋“  3๊ฐœ์˜ ์ž…๋ ฅ์ด ์œ ํšจํ•œ์ง€ ์•„๋‹Œ์ง€์— ๋”ฐ๋ผ true ํ˜น์€ false ๊ฐ’์ž…๋‹ˆ๋‹ค.

๊ทธ๋Ÿฐ ๋‹ค์Œ everythingValid ๋Š” UIButton์˜ reactive extnsion์˜ enabled property์— bind ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ตฌ๋งค ๋ฒ„ํŠผ์˜ ์ƒํƒœ๊ฐ€ everythingValid์˜ ๊ฐ’์— ์˜ํ•ด์„œ ์ œ์–ด๋ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๋ชจ๋“  ์„ธ ๊ฐ’๋“ค์ด ์œ ํšจํ•˜๋‹ค๋ฉด, ๊ธฐ๋ฐ˜ํ•˜๋Š” everythingValid์˜ ๊ฐ’์ด true์ผ ๊ฒ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด, ๊ธฐ๋ฐ˜ํ•œ ๊ฐ’์ด false ์ผ ๊ฒ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๊ฒฝ์šฐ์— rx.enabled๋Š” ๊ตฌ๋งค ๋ฒ„ํŠผ์— ์ ์šฉ๋œ ๊ธฐ๋ฐ˜ ๊ฐ’์ด ์ ์šฉ๋˜๋„๋ก ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์นด๋“œ ์ƒ์„ธ ์ •๋ณด๊ฐ€ ์œ ํšจํ•  ๋•Œ๋งŒ enable ๋ฉ๋‹ˆ๋‹ค.

์ด์ œ setup methods๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค. viewDidLoad()์— ๊ทธ๊ฒƒ๋“ค์„ ์ถ”๊ฐ€ ํ•˜์„ธ์š”.

setupCardImageDisplay()
setupTextChangeHandling()

์•ฑ์„ ๋นŒ๋“œํ•˜๊ณ  ์‹คํ–‰์‹œํ‚ค์„ธ์š”. ์‹ ์šฉ์นด๋“œ ์ž…๋ ฅ์„ ๋ฐ›๊ธฐ ์œ„ํ•ด์„œ ์ดˆ์ฝœ๋ฆฟ์„ ํƒญํ•˜์—ฌ ์นดํŠธ์— ์ถ”๊ฐ€ํ•˜๊ณ , ์นดํŠธ๋กœ ๊ฐ€๊ธฐ์œ„ํ•ด์„œ ์นดํŠธ ๋ฒ„ํŠผ์„ ํƒญํ•˜์„ธ์š”. ์ตœ์†Œ ํ•˜๋‚˜ ์ด์ƒ์˜ ์ดˆ์ฝœ๋ฆฟ์ด ์นดํŠธ์— ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ฒดํฌ์•„์›ƒ ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™” ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์นด๋“œ ์ž…๋ ฅ ํ™”๋ฉด์„ ๋ณด์—ฌ์ค„ ์ฒดํฌ ์•„์›ƒ ๋ฒ„ํŠผ์„ ํƒญํ•˜์„ธ์š”.

Card Number text field์— 4๋ฅผ ํƒ€์ดํ•‘ํ•˜์„ธ์š”. - ์นด๋“œ ์ด๋ฏธ์ง€๊ฐ€ ์ฆ‰์‹œ Visa๋กœ ๋ณ€ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค.

4๋ฅผ ์ง€์šฐ์„ธ์š”. ๊ทธ๋ฆฌ๊ณ  ์นด๋“œ ์ด๋ฏธ์ง€๊ฐ€ unknown์œผ๋กœ ๋ณ€๊ฒฝ๋  ๊ฒ๋‹ˆ๋‹ค. 55๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”. ์ด๋ฏธ์ง€๊ฐ€ MasterCard๋กœ ๋ณ€๊ฒฝ๋  ๊ฒ๋‹ˆ๋‹ค.

๋ฉ‹์ง‘๋‹ˆ๋‹ค! ์ด ์•ฑ์€ ๋ฏธ๊ตญ์—์„œ 4๊ฐœ์˜ ๋ฉ”์ด์ € ํƒ€์ž…์— ๋Œ€ํ•ด์„œ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.(Visa, MasterCard, Amerian Express, Discover). ๋งŒ์•ฝ ์ด ์นด๋“œ ํƒ€์ž…๋“ค์ค‘์—์„œ ํ•˜๋‚˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด, ์ด๋ฏธ์ง€๊ฐ€ ์ œ๋Œ€๋กœ ๋‚˜์˜ค๊ณ  ๋ฒˆํ˜ธ๊ฐ€ ์œ ํšจํ•œ์ง€๋ฅผ ๋ณด๊ธฐ ์œ„ํ•ด์„œ ์นด๋“œ ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•ด๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ฃผ์˜:๋งŒ์•ฝ ์ด๋“ค ์‹ ์šฉ ์นด๋“œ ์ค‘ ํ•˜๋‚˜๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๋‹ค๋ฉด, PayPal()์ด ๊ทธ๋“ค์˜ card sandbox๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ์นด๋“œ ๋ฒˆํ˜ธ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์นด๋“œ ๋ฒˆํ˜ธ๋“ค์€ ๋น„๋ก ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋Š” ์—†์ง€๋งŒ, ์•ฑ์—์„œ ๋ชจ๋“  validation์„ ํ†ต๊ณผ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์œ ํšจํ•œ ์นด๋“œ ๋ฒˆํ˜ธ๊ฐ€ ๋ฏธ๋ž˜์˜ ์–ด๋А ์‹œ๊ฐ„์˜ (2์ž๋ฆฌ ์›”๊ณผ 4์ž๋ฆฌ ๋…„๋„๋ฅผ ์‚ฌ์šฉํ•˜๋Š”)expiration date, ์ ์ ˆํ•œ ๊ธธ์ด์˜ CVV์™€ ํ•จ๊ป˜ ์ž…๋ ฅ๋˜๋ฉด, Buy Chocolate! ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™”๋  ๊ฒ๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ๋ถ„์ด ์‚ฐ ๊ฒƒ๊ณผ ์–ผ๋งˆ๋‚˜ ์ง€๋ถˆํ–ˆ๋Š”์ง€์— ๋Œ€ํ•œ ์š”์•ฝ ๊ทธ๋ฆฌ๊ณ  ์ž‘์€ ์ด์Šคํ„ฐ ์—๊ทธ๋ฅผ ๋ณด๋ ค๋ฉด, ๊ตฌ๋งค ๋ฒ„ํŠผ์„ ํƒญํ•˜์„ธ์š”.

์ถ•ํ•˜ํ•ฉ๋‹ˆ๋‹ค! RxSwift์™€ RxCocoa์—๊ฒŒ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ๋ถ„์€ ์น˜๊ณผ์˜์‚ฌ๊ฐ€ ๋ญ๋ผ๊ณ  ํ•˜์ง€ ์•Š์„ ๋งŒํผ ์ดˆ์ฝœ๋ฆฟ์„ ์‚ด์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ๋กœ ๋ถ€ํ„ฐ ๊ฐ€์•ผ ํ•  ๊ณณ

์™„๋ฃŒ๋œ ํ”„๋กœ์ ํŠธ๋Š” ์—ฌ๊ธฐ( https://koenig-media.raywenderlich.com/uploads/2016/10/Chocotastic-finished-s3-rxs-3b1.zip )์—์„œ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๋„์ „์„ ์›ํ•œ๋‹ค๋ฉด, ์ด ์•ฑ์„ ์ข€ ๋” reactiveํ•˜๊ฒŒ ์œ„ํ•ด์„œ ๋ช‡๊ฐ€์ง€ ์ถ”๊ฐ€ํ•ด ๋ณด์„ธ์š”.

*CartViewController๋ฅผ (label ๋Œ€์‹ ์—)reactive table view๋ฅผ ์ด์šฉํ•ด์„œ ์นดํŠธ์˜ ๋‚ด์šฉ์„ ํ‘œ์‹œํ•˜๋„๋ก ์ˆ˜์ •ํ•ด ๋ณด์„ธ์š”.

*์œ ์ €๊ฐ€ ์นดํŠธ์—์„œ ๋ฐ”๋กœ ์ดˆ์ฝœ๋ฆฟ์„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด์„ธ์š”. ๊ฐ€๊ฒฉ๋„ ์ž๋™์œผ๋กœ ์—…๋ฐ์ดํŠธ ๋˜๋„๋ก ํ•˜๊ตฌ์š”.

์ด์ œ Rx programming์˜ ๋ง›์„ ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ ๋‹น์‹ ์—๊ฒŒ ๋‹น์‚ฐ์˜ ์—ฌํ–‰์„ ๊ณ„์†ํ•˜๋„๋ก ๋„์šธ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋” ์žˆ์Šต๋‹ˆ๋‹ค.

*RxSwift Slack( http://slack.rxswift.org/ )

*RxSwiftโ€™s Getting Started guide( https://github.com/ReactiveX/RxSwift/blob/master/Documentation/GettingStarted.md )

*Max Alexanderโ€™s talk on Rx at Realm ( https://realm.io/news/slug-max-alexander-functional-reactive-rxswift/ )

๋งˆ์ง€๋ง‰์œผ๋กœ, Marin Todorov( https://realm.io/news/slug-max-alexander-functional-reactive-rxswift/ )๋Š” rx_marin( http://rx-marin.com/ )์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋Š” Reactive programming์— ์žˆ์–ด์„œ ๊ทธ์˜ ๋ชจํ—˜์— ๋Œ€ํ•œ ํ›Œ๋ฅญํ•œ ๋ธ”๋กœ๊ทธ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ™•์ธํ•ด ๋ณด์„ธ์š”!

๋งŒ์•ฝ ์ด ํŠœํ„ฐ๋ฆฌ์–ผ์„ ํ†ตํ•ด์„œ ๋ฐฐ์šฐ๋Š” ๊ฒƒ์ด ์ฆ๊ฑฐ์šฐ์…จ๋‹ค๋ฉด, ์Šคํ† ์–ด์— ์žˆ๋Š” RxSwift book( https://store.raywenderlich.com/products/rxswift?_ga=1.9745141.1558722521.1451401789 )์„ ํ™•์ธํ•ด๋ณด๋Š” ๊ฒƒ์€ ์–ด๋– ์„ธ์š”?

์ด๊ฒƒ์€ ์ด ์ฑ…์— ์žˆ๋Š” ๋ง›๋ณด๊ธฐ ์ž…๋‹ˆ๋‹ค.:

  • ์‹œ์ž‘ํ•˜๊ธฐ: Reactive programming ํŒจ๋Ÿฌ๋‹ค์ž„์— ๋Œ€ํ•œ ์†Œ๊ฐœ๋ฅผ ์–ป๊ณ , ํฌํ•จ๋œ ์šฉ์–ด๋ฅผ ๋ฐฐ์šฐ์„ธ์š”. ๊ทธ๋ฆฌ๊ณ  ์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ์—์„œ ์–ด๋–ป๊ฒŒ RxSwift๋ฅผ ์‹œ์ž‘ํ• ์ง€ ์•Œ์•„ ๋ณด์„ธ์š”.

  • Event Management: Rx- Observable๋“ค๊ณผ Observer๋“ค์˜ ๋‘๊ฐ€์ง€ ๊ฐœ๋…์„ ๊ฐ€์ง€๊ณ  ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ sequence๋“ค์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ฐฐ์šฐ์„ธ์š”.

  • Selective ๋˜๊ธฐ: filter, transform, combine, ๊ณผ time operator๋“ค๊ณผ ๊ฐ™์€ ๊ฐœ๋…์„ ์ด์šฉํ•ด์„œ ๋‹ค์–‘ํ•œ ์ด๋ฒคํŠธ๋“ค์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„ ๋ณด์„ธ์š”.

  • UI Development: RxSwift๋Š” RxCocoa๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์•ฑ์˜ UI์ž‘์—…์„ ์‰ฝ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. RxCocoa๋Š” Cocoa์™€ UIKit ๋‘˜์˜ ํ†ตํ•ฉ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • intermediate Topics: reactive networking, multi-threading, error handling์— ๊ด€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ RxSwift ์ง€์‹์„ ๋ ˆ๋ฒจ์—…ํ•˜์„ธ์š”.

  • Advanced Topcis: MVVM ์•ฑ ๊ตฌ์กฐ, scene-based navigation, service๋“ค์„ ํ†ตํ•œ ๋ฐ์ดํ„ฐ ๋…ธ์ถœ์— ๋Œ€ํ•ด ๋ฐฐ์›€์œผ๋กœ์จ RxSwift ๊ต์œก์„ ๋งˆ๋ฌด๋ฆฌ ์ง€์šฐ์„ธ์š”.

  • ๊ทธ๋ฆฌ๊ณ  ๋” ๋” ๋”!

์ด ์ฑ…์˜ ๋ ์ฏค์— reactive ํŒจ๋Ÿฌ๋‹ค์ž„์—์„œ ์ผ๋ฐ˜์ ์ธ ์ด์Šˆ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ์‹ค์ œ ๊ฒธํ—˜์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - ๊ทธ๋ฆฌ๊ณ  ์—ฌ๋Ÿฌ๋ฒˆ ์ž์‹ ์˜ Rx ํŒจํ„ด๊ณผ ์†”๋ฃจ์…˜๋“ค์„ ์ƒ๊ฐํ•ด ๋‚ด๋Š”๋ฐ ์ œ๋Œ€๋กœ ์ ‘๊ทผํ•˜๊ฒŒ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์งˆ๋ฌธ์ด ์žˆ์œผ์‹œ๊ฑฐ๋‚˜ ์ œ๊ณตํ•  ๋‹ค๋ฅธ Rx ๋ฆฌ์†Œ์Šค๊ฐ€ ์žˆ์œผ์‹œ๋‹ค๋ฉด? ์•„๋ž˜์— ์ฝ”๋ฉ˜ํŠธ๋ฅผ ๋‚จ๊ฒจ ์ฃผ์‹œ๊ฑฐ๋‚˜ ํฌ๋Ÿผ์— ๋‚จ๊ฒจ ์ฃผ์„ธ์š”.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.