MVVM으로 전환하기 전에 프로토콜로 MVVM 패턴의 기본적인 구조를 정의해보겠습니다.
기본적인 구조를 잡는데 이 포스트를 참고했습니다.
먼저 뷰모델 계층을 추상화할 ViewModelProtocol
을 정의합니다.
protocol ViewModelProtocol {
associatedtype View: ViewProtocol
var view:View? { set get }
}
ViewModelProtocol
은 associatedtype View은 뒤에서 나올 ViewProtocol
의 상세 타입을 정의합니다.
assotiatedtype으로 정의된 View는 뷰모델의 데이터를 시각적으로 보여줄 뷰를 참조합니다.
다음으로 뷰 계층을 추상화할 ViewProtocol
을 정의합니다.
ViewProtocol
은 assocatatedtype ViewModel을 특정 뷰모델의 타입으로 참조하게 됩니다.
bindViewModel()
함수는 뷰모델과 뷰가 화면에 보여지고 나서 초기화하는 역할을 하는 함수로 선언했습니다.
protocol ViewProtocol {
associatedtype ViewModel:ViewModelProtocol
var viewModel:ViewModel? { set get }
func bindViewModel()
}
초기화를 위한 코드는 ViewProtocol이 UIViewController에서 채택되었을 때만 호출할 수 있도록 extension
을 정의합니다.
// UIViewController에서 채택되었을 때
extension ViewProtocol where Self:UIViewController {
mutating func bind(viewModel: Self.ViewModel) {
self.viewModel = viewModel
loadViewIfNeeded()
bindViewModel()
}
}
extension의 조건을 Self:UIViewController
로 정의해 bind(viewModel:Self.ViewModel)
함수가 UIViewController에서만 정의되도록 선언합니다.
그리고 이 함수에서 앞에 선언된 bindViewModel()
을 호출해 뷰가 생성되고 나서 바로 뷰 모델과 바인드합니다.
// ViewProtocol이 UIViewController에서 채택되었을 때
extension ViewModelProtocol where View:UIViewController {
mutating func intializeScene() -> View {
// View 초기화
var view = View.init()
self.view = view
/*
뷰를 초기화하고 나서 자신의 타입이
View의 ViewModel 타입과 동일한지 캐스팅 후
뷰 모델을 참조하고 있는 뷰에 바인딩합니다.
*/
if let viewModel = self as? Self.View.ViewModel {
view.bind(viewModel: viewModel)
}
return view
}
}
MainViewController
라는 UIViewController를 정의한다고 가정하겠습니다.
MainViewController
을 추상화할 MainViewProtocol
을 먼저 정의하겠습니다.
protocol MainViewProtocol:ViewProtocol {
}
MainViewController
의 ViewModel을 추상화할 MainViewModel
의 프로토콜 MainViewModelProtocol을 정의합니다.
MainViewController
의 ViewModel을 추상화할 MainViewModel
의 프로토콜 MainViewModelProtocol을 정의합니다.
MainViewModel
을 정의할 때 ViewModelProtocol.View의 타입을 MainViewProtocol로 지정합니다.
protocol MainViewModelProtocol: ViewModelProtocol where View:MainViewProtocol {
}
ViewController
// 편의상 UIViewController 상속 부분은 제외
class MainUIViewController: MainViewProtocol {
typealias ViewModel = MainViewModel
var viewModel: MainViewModel?
func bindViewModel() {
}
...
}
ViewModel
class MainViewModel: MainViewModelProtocol {
typealias View = MainUIViewController
}
var vm = MainViewModel()
let vc = vm.intializeScene()
self.present(vc, animated:true)