Ответ
MVVM (Model-View-ViewModel) — это архитектурный паттерн, который обеспечивает четкое разделение ответственности, повышая тестируемость и поддерживаемость кода.
Ключевые принципы и преимущества:
-
Разделение слоев:
- Model: Представляет данные и бизнес-логику.
- View (ViewController): Отвечает только за отображение UI и обработку пользовательских действий. «Тупой» слой.
- ViewModel: Преобразует данные Model в значения, удобные для отображения во View. Содержит презентационную логику, но не имеет ссылок на UIKit.
-
Двустороннее связывание данных (Data Binding): Изменения в ViewModel автоматически обновляют View (и наоборот), что устраняет boilerplate-код. В iOS для этого используют Combine, RxSwift или замыкания.
-
Повышенная тестируемость: Поскольку ViewModel не зависит от UI, её логику легко покрыть модульными тестами без необходимости эмуляции интерфейса.
Пример на Swift с Combine:
// MARK: - ViewModel
class LoginViewModel {
@Published var username: String = ""
@Published var password: String = ""
@Published var isValid: Bool = false
private var cancellables = Set<AnyCancellable>()
init() {
$username
.combineLatest($password)
.map { user, pass in
return !user.isEmpty && pass.count >= 8
}
.assign(to: .isValid, on: self)
.store(in: &cancellables)
}
func login() { /* Сетевая логика */ }
}
// MARK: - View
class LoginViewController: UIViewController {
@IBOutlet private weak var loginButton: UIButton!
private let viewModel = LoginViewModel()
private var cancellables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
bindViewModel()
}
private func bindViewModel() {
viewModel.$isValid
.receive(on: DispatchQueue.main)
.assign(to: .isEnabled, on: loginButton)
.store(in: &cancellables)
}
@IBAction func usernameChanged(_ sender: UITextField) {
viewModel.username = sender.text ?? ""
}
}
Когда MVVM может быть избыточен: В очень простых экранах (например, статичный список) классический MVC остается валидным выбором. Сложность внедрения возрастает без использования реактивных фреймворков.