RxSwift/RxSwift 응용
[RxSwift 응용] 2. MVVM with RxSwift
jake-kim
2020. 7. 1. 10:45
MVVM

- ViewModel은 데이터를 바꾸기만 함 (View에 어떤 화면을 그러야 하는지 안알려주고, View가 subscribe하고 있음)
- 그냥 데이터만 갖고 그 값만 바꾸고 있는 애
- UI에 관한 요소는 건들지 않음 —> bug가 발생할 위험은 UI가 아니라 logic적인 부분이므로,, 버그가 발생할 확률이 줄어듦
- Testable이라는 것은 UI정보가 없어야 가능
정리
- ViewContoller : UI에 대한 업데이트만 관리, view model을 subscribe하면서 UI업데이트
- view model : logic에 대한 것만 관리
비교)

1. recall
- View에서 커멘드를 입력 받아, ViewModel에서 데이터를 가공하여 View에 바인딩
- ViewModel쪽에 Reactive로 만들어서 View에 UI를 업데이트 할 수 있게끔 함(bind)

2. 예제

TextField에 입력하면, Label에 업데이트 되는 것
(TextField를 input, Label을 output)
View - UILabel
ViewModel - input과 output정의, 각각 Observable로 사용
1) ViewModel정의
(input과 output으로 추상화하는 protocol)
protocol ViewModelType {
associatedtype Input
associatedtype Output
associatedtype Dependency
var input: Input {get}
var output: Output {get}
var dependency: Dependency {get}
}
(ViewModel정의, input이 들어오면 내부에서 output할 것을 Observable로 생성)
- output할 bind는 controller에서 정의
class SampleViewModel: ViewModelType {
let input: Input
let output: Output
let dependency: Dependency
init(input: Input, dependency: Dependency) {
self.input = input
self.dependency = dependency
let rxResult = input.text.asObservable()
self.output = Output(result: rxResult)
}
}
extension SampleViewModel {
struct Input {
let text: Observable<String>
}
struct Output {
let result: Observable<String>
}
class Dependency {
let service: SomeService // 서비스를 처리하는 프로퍼티
}
}
2) bind정의
class ViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var label: UILabel!
var viewModel: SampleViewModel!
let db = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bind()
}
private func bind() {
// rx.text할 경우 RxCocoa필요
let input = SampleViewModel.Input(text: textField.rx.text.orEmpty.asObservable())
guard let text = label.text else {return}
viewModel = SampleViewModel(input: input, dependency: SomeDependency())
viewModel.output.result
.bind(to: label.rx.text)
.disposed(by: db)
}
}