개발 프로세스에서…

보통은 기획서 ( 어떤 기능 있는지 ) 나온 뒤 BE, Design 작업 완료되면 그에 따라서 개발하는 것으로 생각 → 동시에 개발할 수는 없을까 ?

‘기능’ 위주로 먼저 개발하고, 디자인이 나오면 그에 맞게 개발 백엔드 나오면 그에 맞게 모델명 변경하면서 개발

바로 디자인이랑 서버에 붙일 수 있도록 구현해보자

simulator_screenshot_40E69402-F3A0-4FDC-AA5E-6447F197741A.png

해당 뷰를 표시하기 위해서는

이렇게 총 3가지가 필요

struct Menu {
		var name: String
		var count: Int
		var price: Int
}

→ View 의 Model 이기 때문에 ViewModel 이라 힘

MenuListViewModel 생성

class MenuListViewModel {
  
    var menus: [Menu] = [
        .init(name: "튀김1", count: 0, price: 1000),
        .init(name: "튀김1", count: 0, price: 1000),
        .init(name: "튀김1", count: 0, price: 1000),
        .init(name: "튀김1", count: 0, price: 1000),
    ]

		var totalPrice: Int = 0
		var itemsCount: Int = 0
    
}

버튼을 눌렀을 때 UI 가 변경되도록 하려면 현재 상황에서는 버튼을 누를 때마다 하단 func 가 실행되어야 함

func updateUI() {
		viewModel.totalPrice += 100
		self.totalPrice.text = "\\(viewModel.totalPrice)"
}

이런 반복적인 함수 실행을 막기 위해 RxSwift 사용

override func viewDidLoad() {
    super.viewDidLoad()
    
    viewModel.totalPrice
        .map { $0.currencyKR() }
        .subscribe(onNext: {
            self.totalPrice.text = $0
        })
        .disposed(by: disposeBag)
}

🚨 여기서 문제 발생 🚨

버튼을 누를 때마다 VM 의 totalPrice 가 변경되어야 하는데, Observable<Int> 타입이어서 안됨 → Observable 은 값을 받아오는 애이기 때문에 외부에서 컨트롤 할 수는 없음

이걸 해결하기 위해 나온게 SubjectSubjectObservable 처럼 외부에서 값을 받아올 수도 있고, 통제할 수도 있음

class MenuListViewModel {
  
    var menus: [Menu] = [
        .init(name: "튀김1", count: 0, price: 1000),
        .init(name: "튀김1", count: 0, price: 1000),
        .init(name: "튀김1", count: 0, price: 1000),
        .init(name: "튀김1", count: 0, price: 1000),
    ]

		// var totalPrice: Observable<Int> = 0
		**var totalPrice: PublishSubject<Int> = PublishSubject()**
		var itemsCount: Int = 0
    
}

이렇게 정의한다면, 아래와 같은 프로세스 가능

func onOrder() {
		viewModel.totalPrice.onNext(100)
}

위와 같이 진행하면, VM 의 totalPrice 를 100 으로 설정

PublishSubject 란?

BehaviorSubject 란?

AsyncSubject 란?

ReplaySubject 란?

그러면 눌렀을 때마다 100씩 추가시키려면 ?

viewModel.totalPrice
		**.scan(0, accumulator: +)**
    .map { $0.currencyKR() }
    .subscribe(onNext: {
        self.totalPrice.text = $0
    })
    .disposed(by: disposeBag)

.scan 을 사용 → 0부터 시작해서, 새로운 값이 들어오면 기존 값에 더해준다 → 100, 200, 300, ….

이전에 updateUI() 메소드를 실행할 때에는, self.totalPrice.text = $0 를 계속 실행하여 업데이트 하는 식이었는데,

이런 방식으로 바꾸면, subscribe 를 한 번만 하면, onNext 가 호출 될 때마다 알아서 업데이트를 시켜줌 → updateUI() 를 매번 호출해주지 않아도 됨

따라서 아래와 같이 선언해두면, 따로 textLabel 의 text 값을 변경시키는 코드를 매번 실행시키지 않더라도 알아서 UI 가 업데이트 됨

override func viewDidLoad() {
    super.viewDidLoad()
    
    viewModel.itemsCount
				.map { "\\($0)" }
        .subscribe(onNext: {
            self.itemCountLabel.text = $0
        })
        .disposed(by: disposeBag)
    
    viewModel.totalPrice
        .map { $0.currencyKR() }
        .subscribe(onNext: {
            self.totalPrice.text = $0
        })
        .disposed(by: disposeBag)
}
class MenuListViewModel {
    var menuObservable = PublishSubject<[Menu]>()
    lazy var itemsCount = menuObservable.map {
        $0.map { $0.count }.reduce(0, +)
    }
    lazy var totalPrice = menuObservable.map {
        $0.map { $0.count * $0.price }.reduce(0, +)
    }
    
    init() {
        let menus: [Menu] = [
            .init(name: "튀김1", count: 0, price: 1000),
            .init(name: "튀김1", count: 0, price: 1000),
            .init(name: "튀김1", count: 0, price: 1000),
            .init(name: "튀김1", count: 0, price: 1000),
        ]
        
        menuObservable.onNext(menus)
    }
}

이와 같이 menuObservable 하나의 변화가 itemsCounttotalPrice 를 조절하게 되는데, 이런 흐름을 Stream 이라고 함