ํฐ์คํ ๋ฆฌ ๋ทฐ
[RxSwift] #5) RxSwift + MVVMํจํด [Subject๋ฅผ ํ์ฉํด๋ณด์]
ir.__.si 2023. 1. 30. 07:28๋ณธ ํฌ์คํ ์ ๊ณฐํ๊น๋์ ๊ฐ์์์์ ๊ธฐ๋ฐ์ผ๋ก, ๊ฐ์ธ์ ์ผ๋ก ๊ณต๋ถํ ๋ด์ฉ์ ์ ๋ฆฌํ ๊ธ์ ๋๋ค.
๋์ฑ ์์ธํ ๋ด์ฉ์, ๊ฐ์ ์์์ ์ง์ ์์ฒญํ์๋๊ฒ์ ์ถ์ฒ๋๋ฆฝ๋๋ค!
์ด์ ์ ํฌ์คํ ํ [RxSwift] #3) RxSwift๋ฅผ ํ์ฉํ MVVMํจํด [1] ์ ์ด์ด์ง๋ ํฌ์คํ ์ ๋๋ค.
๋ํ, ์ด๋ฒ ๊ธ๋ถํฐ ์ฝ๋๋ด์ฉ์ ์บก์ณ ํ์์ด ์๋, ์ฝ๋๋ธ๋ญ ํ์์ผ๋ก ์์ฑ ํ ์์ ์ ๋๋ค!
์ด์ ํฌ์คํ ์์ Observable๋ก ์์ฑํ ๋ณ์๋ฅผ, ๊ตฌ๋ ํ ์ดํ์๋ ์ง์์ ์ธ ๊ฐ์ ๋ณ๊ฒฝ์ ๊ด์ฐฐํด์ผํ๋ ํ์๊ฐ ์๊ฒจ
์ด๋ฅผ์ํด Observable์ Subject๋ก ๊ตฌํํด์ผ ํ ํ์๊ฐ ์๋ค๊ณ ํ์์ต๋๋ค.
(Subject์ ๋ํ ์ค๋ช ์ ์ด ๊ฒ์๋ฌผ์ ์ฐธ๊ณ ํด์ฃผ์ธ์!)
class MenuListViewModel{
var menus : [Menu] = [
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0)
]
var itemsCount : Int = 5
var totalPrice : PublishSubject<Int> = PublishSubject()
}
Observable ๋์ Subject๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด, ๊ฐ์ฅ ๋ณดํธ์ ์ผ๋ก ๋ง์ด ์ฌ์ฉํ๋ PublishSubject๋ฅผ ์ ์ฉํ์์ต๋๋ค.
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
viewModel.totalPrice
.map{$0.currencyKR()}
.subscribe(onNext: {self.totalPrice.text = $0})
.disposed(by: disposeBag)
}
@IBAction func onOrder(_ sender: UIButton) {
// TODO: no selection
// showAlert("Order Fail", "No Orders")
// performSegue(withIdentifier: "OrderViewController", sender: nil)
viewModel.totalPrice.onNext(100)
}
๊ทธ๋ฆฌ๊ณ
1) PublishSubject๋ฅผ ํตํด์ 100์ด๋ผ๋ ๊ฐ์ ๋ฟ๋ ค์ฃผ๋ฉด,
2) .subscribe๋ฅผ ํตํด ๊ฐ์ Label์ ๋ฟ๋ ค์ฃผ๊ณ ์์ต๋๋ค.
ํ์ง๋ง, ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ ์ด 100์ด๋ผ๋ ๊ฐ์ด ์๋๋ผ, ORDER๋ฒํผ์ ๋๋ฅด๋ฉด ๊ฐ์ด ๊ณ์ ์ฆ๊ฐํ๋ ๊ฒ์ ์ํฉ๋๋ค.
๋ฐ๋ผ์ .scan() ์ด๋ผ๋ Operator๋ฅผ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
override func viewDidLoad() {
super.viewDidLoad()
updateUI()
viewModel.totalPrice
.scan(0, accumulator:+)
.map{$0.currencyKR()}
.subscribe(onNext: {self.totalPrice.text = $0})
.disposed(by: disposeBag)
}
.scan์, seed๊ฐ์ธ 0์ ๊ฐ์ด ๋ค์ด์ฌ ๋ ๋ง๋ค + ํ ๊ฐ์ ๋ฐฉ์ถํ๊ฒ ๋ฉ๋๋ค.
์ฆ, 100 -> 200 -> 300 ... ์ด๋ฐ์์ผ๋ก ๊ณ์ ์ฆ๊ฐ๋ ๊ฐ์ ๋ฐฉ์ถํฉ๋๋ค.
์ฒ์ updateUI() ํจ์๋ฅผ ์ฌ์ฉํ์์ ๋, ๊ฐ์ด ๋ฐ๋ ๋ ๋ง๋ค updateUI()๋ฅผ ํธ์ถํด์ผ ํ์ต๋๋ค.
ํ์ง๋ง, PublishSubject๋ฅผ ์ฌ์ฉํ๋ฉด .subscribe() ํ๋ฒ์ผ๋ก ๊ฐ์ด ๋ฐ๋ ๋ ๋ง๋ค ์๋์ผ๋ก ๊ฐ๊ณผ UI๊ฐ ๋ฐ๋๋๋ค.
๋ฐ๋ผ์ ๊ธฐ์กด์ ๋ง๋ค์ด ๋, updateUI()๋ฅผ ์ญ์ ํด์ฃผ๊ฒ ์ต๋๋ค.
var menus : [Menu] = [
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0)
]
var totalPrice : PublishSubject<Int> = PublishSubject()
๊ทธ๋ฆฌ๊ณ ์ฌ์ค ์ด totalPrice๋ผ๋ ๊ฐ์ menus์ ๊ฐ Element๋ค์ธ Menu์ (price * count)์ ํฉ์ ๋๋ค.
๊ทธ๋ ๋ค๋ฉด ์ menus์ ๋ํ Observable์ด ์๋ค๋ฉด, ๊ฐ Element์ ์ ๊ทผํด์ price*count์ ๊ฐ์ ๋นผ์ฌ ์ ์์ง ์์๊น์??
class MenuListViewModel{
var menus : [Menu] = [
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0)
]
lazy var menusObservable = Observable.just(menus)
var itemsCount : Int = 5
lazy var totalPrice = menusObservable
.map{$0.map{$0.price * $0.count}.reduce(0,+)}
}
lazy ๋ณ์๋ก ๋ง๋ค์ด์ฃผ๋ ์ด์ ๋, menus์ menusObservable์ ๋ฉค๋ฒ๋ณ์๋ก ์กด์ฌํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
(lazy ํค์๋ ๊ด๋ จ ์์ธํ ์ค๋ช ์ ์ด ๊ฒ์๋ฌผ์ ์ฐธ๊ณ ํด์ฃผ์ธ์!)
์ ์ฝ๋์ฒ๋ผ, menus๊ฐ ๊ฐ์ง ๊ฐ Menu๋ค์ count*price๋ฅผ ๋ชจ๋ ๋ํด์ totalPrice์ ์ ๋ฌํด์ค๋๋ค.
ํ๋ฒ, ์์ ์ฝ๋์ ํ๋ฆ์ ๋ฐ๋ผ๊ฐ๋ด ์๋ค.
1) menus์ ์์์ธ Menu๋ค์ count๋ price๊ฐ ๋ณํ๋ค.
2) menusObservable์ด Onservable.just()๋ฅผ ํด์ค์ผ๋ก์จ, menus์ ์ํ๋ฅผ ๊ณ์ ๊ด์ฐฐํ๊ณ ๋ฐ์ํ๋ค.
3) totalPrice๋, menusObservable์ด ๊ณ์ ๊ด์ฐฐํ๊ณ ์๋ ๊ฐ์ ์ด์ฉํ์ฌ ํฉ๊ณ ๊ธ์ก์ ๊ณ์ฐํ๊ณ ์ ์ฅํ๋ค.
๊ทธ๋ผ ๊ฐ์ ๋ฐฉ์์ผ๋ก itemsCount๋ ํ๋ฒ ๋ค์ ๋ง๋ค์ด๋ณผ๊น์??
lazy var itemsCount = menusObservable
.map{$0.map{$0.count}.reduce(0,+)}
์์ ๊ฐ์ด itemsCount๋ฅผ ๋ง๋ค์ด ์ค๋ค๋ฉด, Menu๋ค์ ๊ฐฏ์๊ฐ ๋ฐ๋ ๋ ๋ง๋ค ์๋์ผ๋ก ์ ์ฒด ๊ฐฏ์๊ฐ ๊ณ์ฐ๋์ด ์ ์ฅ๋ ๊ฒ์ ๋๋ค!
์, ๊ทธ๋ ๋ค๋ฉด ์๋์ ์ฝ๋๋ ๋ญ๊ฐ ๋ฐ๋์ด์ผ ํ ๊ฒ ๊ฐ์ง ์๋์??
lazy var menusObservable = Observable.just(menus)
๋ง์ต๋๋ค, ์ฐ๋ฆฌ๊ฐ ์ํ๋ ๊ฒ์ ๊ฒฐ๊ตญ menusObservable์ ๊ฐ์ ๊ฐ์ง๊ณ ๋ ธ๋๊ฒ ์ ๋๋ค!
ํ์ง๋ง, ํ์ฌ๋ Observableํ์ ์ผ๋ก ์ ์๊ฐ ๋์ด์๊ธฐ ๋๋ฌธ์ ํ๋ฒ just๋๊ณ ๋์๋ ๊ฐ์ด ๋ฐ๋๋๊ฒ ๊ฐ์งํ ์ ์์ด์!
๊ทธ๋ ๊ธฐ ๋๋ฌธ์, ๊ฐ์ ๋ฐฉ์ถ ํ ์ดํ์๋ ๊ฐ์ ๋ณ๊ฒฝ์ ๊ฐ์งํ ์ ์๋ Subject๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค!
(์ดํดํ๊ธฐ ์ฝ๊ฒ ์ค๋ช ํ๊ฑฐ๋ผ, ์ค์ ์ ์์๋ ์ฌ์์ง ๋๋์ด ๋ค๋ฆ ๋๋ค..!)
๊ทธ ์ค์์๋ ์ฐ๋ฆฐ PublishSubject๋ฅผ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
๋ํ, Subject๋ฅผ ์ฌ์ฉํ๋ค๋ ๊ฒ ์์ฒด๊ฐ ๊ณ์ ๋ฐ๋๋ ๊ฐ์ ๊ฐ์งํ๊ฒ ๋ค๋ ์๋ฏธ์ด๋ฏ๋ก
init()์ ํตํด, ์ฒ์ menus๋ฅผ ์ด๊ธฐํํ๊ณ ๊ทธ ๋ฐ์ดํฐ๋ฅผ PublishSubject๊ฐ .subscribe ํ๋ค๋ฉด
(์๋์ ์ฝ๋์์๋ menusObservable์ด operator์ธ .onNext()๋ฅผ ์ฌ์ฉํ๊ณ ์์ต๋๋ค.)
์ดํ์ ์ค์๊ฐ์ผ๋ก ๋ฐ๋๋ ๊ฐ๋ค์ ๊ณ์ ๊ด์ฐฐ ํ ์๊ฐ ์์ต๋๋ค.
class MenuListViewModel{
var menusObservable = PublishSubject<[Menu]>()
lazy var itemsCount = menusObservable
.map{$0.map{$0.count}.reduce(0,+)}
lazy var totalPrice = menusObservable
.map{$0.map{$0.price * $0.count}.reduce(0,+)}
init(){
var menus : [Menu] = [
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0),
Menu(name: "ํ๊น1", price: 100, count: 0)
]
menusObservable.onNext(menus)
}
}
์ด๋ ๊ฒ ํ๋ฉด, ์์ ๊ฐ์ MenuListViewModel์ ์ ์ฒด ์ฝ๋๊ฐ ์์ฑ๋ฉ๋๋ค.
๊ทธ๋ผ ์ด์ , ์ค์ View๋ฅผ ๊ทธ๋ ค์ฃผ๋ MenuViewController์ ๊ฐ์, ์ค์ View์ ์ ์ฉ์์ผ ์ค์๋ค.
์ฐ์ viewDidLoad()์์ Observable๋ค์ ๊ตฌ๋ (.subscribe)ํด์ค์๋ค.
let viewModel = MenuListViewModel()
let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
viewModel.itemsCount
.subscribe(onNext: {self.itemCountLabel.text = "\($0)"})
.disposed(by: disposeBag)
viewModel.totalPrice
.map{$0.currencyKR()}
.subscribe(onNext: {self.totalPrice.text = $0})
.disposed(by: disposeBag)
}
์ด๋ ๊ฒ viewDidLoad์ ๊ตฌ๋ ์ ๋ชจ๋ํด๋์ผ๋ฉด, ์ดํ์ ๊ฐ์ด ๋ฐ๋ ๋ ๋ง๋ค ์์์
1) totalPrice
2) itemsCount
์ 2๊ฐ๊ฐ View์ ์ ๋ฐ์ดํธ๊ฐ ๋ฉ๋๋ค!
๊ทธ๋ฐ๋ฐ, ์ ๋ณด๋ฉด UITableView์ protocol๋ค์๋ ์๋์ ๊ฐ์ด ๋นจ๊ฐ์ค์ด ๋ ์์ฃ ??
์ฌ์ค, ์ด๋ฐ Delegateํจํด์ ์ฌ์ฉํด์ protocol์ ๊ตฌํํ๋ UI๋ค์ ๊ฒฝ์ฐ๋, Rx๊ฐ ํ์์ ์ธ ๊ฒ์ ์๋๋๋ค.
์๋ํ๋ฉด, ์ด๋ฏธ Delegateํจํด์ ํตํด์ ๋์ผํ ๊ธฐ๋ฅ๋ค์ด ๋ค ๊ตฌํ์ด ๋์ด์๊ฑฐ๋ ์,,,
ํ์ง๋ง, Rx๋ฅผ ์ฌ์ฉํ๋ ๊ฐ์ฅ ํฐ ์ด์ ์ค์ธ ํ๋์ธ ์ฝ๋์ ๊ฐ๊ฒฐ์ฑ์ ์ํด์๋ Rx๋ฅผ ์ ๋ชฉ์์ผ์ค์ผ ํฉ๋๋ค!
๊ทธ๋ฆฌ๊ณ ์ด๋ฌํ Delegate์ ์ ์๋ protocol์ Rx๋ก ๋์ฒดํด์ฃผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ฐ๋ก
RxCocoa์ ๋๋ค!!
์ด๋ฌํ RxCocoa๋ฅผ UITableView์ ์ ๋ชฉ์ํค๋ฉด, ์ ์ฌ์ง์ ๋ณด์ด๋ Delegate๋ฅผ ๋ชจ์กฐ๋ฆฌ ์์ ๋ฒ๋ฆด ์ ๊ฐ ์์ต๋๋ค...
์ด์ ๊ด๋ จ๋ ๋ด์ฉ์ ๋ค์ ํฌ์คํ ์์ ์ ๋ฆฌํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค!!
'๐ > RxSwift' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[RxSwift] #7) Driver๋ ๋ฌด์์ธ๊ฐ?? [Driver์ Relay] (0) | 2023.02.06 |
---|---|
[RxSwift] #6) RxSwift + MVVMํจํด [RxCocoa๋ฅผ ํ์ฉํ UITableView ๊ตฌ์ฑ] (2) | 2023.02.01 |
[RxSwift] #4) Subject๋ ๋ฌด์์ผ๊น? (1) | 2023.01.29 |
[RxSwift] #3) RxSwift + MVVMํจํด [๊ธฐ๋ณธ ํ๋ก์ ํธ ๊ตฌ์ฑ] (0) | 2023.01.29 |
[RxSwift] #2) Operator๋?? + ReactiveX ๋ฌธ์๋ณด๋ ๋ฒ (2) | 2023.01.28 |