관리 메뉴

김종권의 iOS 앱 개발 알아가기

[RxSwift 응용] 2. MVVM with RxSwift 본문

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)
    }
}

 

Comments