private let mainViewModel: MainViewModel
이 방식은 의존성 주입이나 지연 초기화에서 자주 사용된다.
mainViewModel
을 선언만 해두고 나중에 외부에서 주입한다.
예
class ProfileViewController: UIViewController {
private let mainViewModel: MainViewModel
init(viewModel: MainViewModel) {
self.mainViewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
updateProfile()
}
private func updateProfile() {
nameLabel.text = mainViewModel.userName
emailLabel.text = mainViewModel.userEmail
profileImageView.image = mainViewModel.profileImage
}
}
이 방식으로 ProfileViewController
생성 시 외부에서 ViewModel
주입 가능.
private let mainViewModel = MainViewModel()
간단하고 즉시 사용 가능한 것이 장점이다.
그러나 만약 ViewModel
이라는 클래스의 로직이 복잡해지면 관리가 어려워질 수 있다.
예
class DashboardViewController: UIViewController {
private let mainViewModel = MainViewModel()
override func viewDidLoad() {
super.viewDidLoad()
setupDashboard()
}
private func setupDashboard() {
userGreetingLabel.text = "안녕하세요, \(mainViewModel.userName)!"
taskCountLabel.text = "할 일: \(mainViewModel.pendingTasksCount)개"
lastLoginLabel.text = "최근 로그인: \(mainViewModel.lastLoginDate)"
}
}
개발자는 항상 수정과 기능의 확장을 염두해둬야하기 때문에 두번째 방법은 지양하는게 맞는 것 아닐까?
// 1. 커피만들기에 필요한 클래스 정의
class CoffeeBeans { // 커피콩과
let type: String
init(type: String) { self.type = type }
}
class Water { // 물이 필요
let source: String
init(source: String) { self.source = source }
}
// 2. 의존성 주입을 사용하는 CoffeeMachine 클래스
class CoffeeMachine {
private let beans: CoffeeBeans
private let water: Water
init(beans: CoffeeBeans, water: Water) {
self.beans = beans
self.water = water
}
func brewCoffee() -> String {
return "커피가 만들어졌어요: \(beans.type) 원두와 \(water.source) 물로 만든 커피"
}
}
// 3. CoffeeMachine을 사용하는 CafeManager 클래스
class CafeManager {
private let coffeeMachine: CoffeeMachine
init(coffeeMachine: CoffeeMachine) {
self.coffeeMachine = coffeeMachine
}
func orderCoffee() -> String {
return coffeeMachine.brewCoffee()
}
}
// 4. 실제 사용 예
let premiumBeans = CoffeeBeans(type: "콜롬비아 수프리모")
let mineralWater = Water(source: "알프스 샘물")
let fancyCoffeeMachine = CoffeeMachine(beans: premiumBeans, water: mineralWater)
let cafeManager = CafeManager(coffeeMachine: fancyCoffeeMachine)
print(cafeManager.orderCoffee())
// 출력: 커피가 만들어졌어요: 콜롬비아 수프리모 원두와 알프스 샘물로 만든 커피
뷰는 없다. 만약 이상황에서
MVC 패턴의 경우
Model: CoffeeBeans, Water 클래스
Controller: CoffeeMachine, CafeManager 클래스
View: -
premiumBeans부터 끝까지의 코드: Controller에서 Model을 초기화하고 조작하는 로직으로 볼 수 있어요.
MVVM 패턴의 경우
Model: CoffeeBeans, Water 클래스
ViewModel: CoffeeMachine, CafeManager 클래스
View: ui는 없다. premiumBeans부터 끝까지의 코드: ViewModel을 초기화하고 사용하는 부분으로, View(ViewController)
ViewModel(CafeManager)이 Model(CoffeeBeans, Water)을 직접 다루는 대신, 데이터를 가공하여 View에 필요한 형태로 제공
View(ViewController)는 ViewModel의 데이터를 관찰하고 표시하며, 사용자 입력을 ViewModel에 전달
실제 MVVM 구현에서는 바인딩이나 리액티브 프로그래밍(*ex. rxSwift, combine)을 사용하여 ViewModel과 View 사이의 데이터 흐름을 자동화하는 경우가 많음
*ex. rxSwift, combine: 리액티브 프로그래밍을 Swift에서 구현한 대표적인 프레임워크