Ответ
MVVM (Model-View-ViewModel) в UIKit разделяет ответственность:
- Model — данные и бизнес-логика.
- View —
UIViewControllerиUIView, отвечающие за отображение. - ViewModel — преобразует Model в данные, готовые для отображения View, и содержит презентационную логику.
Базовая реализация:
// MARK: - Model
struct User {
let name: String
let age: Int
}
// MARK: - ViewModel
class UserViewModel {
private let user: User
// Преобразование данных для View
var displayName: String { "User: (user.name)" }
var displayAge: String { "Age: (user.age)" }
init(user: User) {
self.user = user
}
}
// MARK: - View (ViewController)
class UserViewController: UIViewController {
@IBOutlet private weak var nameLabel: UILabel!
@IBOutlet private weak var ageLabel: UILabel!
var viewModel: UserViewModel! {
didSet { updateView() }
}
private func updateView() {
nameLabel.text = viewModel.displayName
ageLabel.text = viewModel.displayAge
}
}
Ключевые принципы:
- ViewModel не имеет ссылок на UIKit. Она независима от слоя представления.
- View (ViewController) становится "глупым". Он только отображает данные из ViewModel и передаёт ей пользовательские действия.
- Связывание (Binding): Для реактивного обновления View при изменении ViewModel используйте:
- Closures или делегаты для простых случаев.
- Combine фреймворк (iOS 13+) для декларативного биндинга.
- RxSwift или ReactiveCocoa в сторонних проектах.
Преимущества: Упрощается тестирование (ViewModel тестируется без UIKit), уменьшается связанность кода, ViewController становится менее массивным.
Ответ 18+ 🔞
А, MVVM в UIKit, да? Ну это ж классика, блядь! Как эти ваши деревянные матрешки, только в коде, ёпта. Сейчас разложу по полочкам, как нормальный человек, а не как эти заумные учебники, от которых глаза на лоб лезут.
Смотри, вся фишка в том, чтобы растащить ответственность, а то ViewController у нас обычно — такая свалка, что мама не горюй! Он и данные качает, и интерфейсом вертит, и бизнес-логику пытается в себя впихнуть. Пиздец, а не класс. MVVM приходит и говорит: «Так, пацаны, разойдись! Каждый занимается своим делом».
Кто на что учился:
- Model (Модель) — это, по сути, данные. Голые, как сосиски из холодильника. Имя, возраст, флаги какие-нибудь. Никакой тебе красоты, просто факты.
- View (Вьюха) — это наш
UIViewControllerсо всеми егоUILabel,UIButton. Его задача — тыкать пальцем в экран и показывать, что ему сказали. Больше ни-ху-я. Идеальный солдат. - ViewModel (ВьюМодель) — вот тут вся магия, блядь! Это такой переводчик-секретарь. Берёт голые данные из Model, делает из них красивую строку для лейбла, решает, какую кнопку скрыть, а какую показать. Вся презентационная логика — тут.
Вот, смотри, как это выглядит в коде, без этих твоих заумных маркеров:
// Это наша сосиска-модель. Данные и всё.
struct User {
let name: String
let age: Int
}
// А это — наш главный затейник, ViewModel.
class UserViewModel {
private let user: User // Держит модельку при себе.
// Он уже приготовил данные, разжевал и в рот положил.
var displayName: String { "Юзер: (user.name)" }
var displayAge: String { "Возраст: (user.age)" }
init(user: User) {
self.user = user
}
}
// Ну и сам ViewController, который стал внезапно очень простым.
class UserViewController: UIViewController {
@IBOutlet private weak var nameLabel: UILabel!
@IBOutlet private weak var ageLabel: UILabel!
// Приходит ViewModel — и сразу обновляемся!
var viewModel: UserViewModel! {
didSet { updateView() }
}
private func updateView() {
nameLabel.text = viewModel.displayName // Берём готовенькое!
ageLabel.text = viewModel.displayAge // И тут берём!
}
}
А теперь главные правила, которые нарушать — себя не уважать:
- ViewModel НЕ ДОЛЖНА ЗНАТЬ ПРО
UIKit. Вообще. НикакихUILabel,UIViewController. Она живёт в своём прекрасном мире строк, чисел и булевых флагов. Если ты импортируешьUIKitв ViewModel — ты проебалася. - ViewController превращается в тупого исполнителя. Его удел — взять данные из ViewModel и запихнуть их в аутлеты. Или отловить нажатие кнопки и сказать об этом ViewModel. Всё! Никаких
if-elseна три экрана про то, скрывать ли кнопку. - Связывание (Binding). Вот это, блядь, самый сок. Чтобы не тыкать кнопкой
updateView()каждый раз, используют биндинг. ViewModel меняется — View автоматически обновляется. Как это сделать?- На коленке: closures или делегаты. Для мелких проектов сойдёт.
- По-взрослому (iOS 13+): фреймворк Combine. Это охуенно, но надо голову настроить.
- Из стороннего: RxSwift или ReactiveCocoa. Мощно, но это отдельная религия.
И что в сухом остатке, спросишь ты? А то, что тестировать эту хуйню становится в разы проще. ViewModel можно гонять юнит-тестами, потому что в ней нет никакого UIKit. ViewController, который теперь тупой как пробка, можно не тестировать вообще, либо тестировать через UI-тесты. И главное — в коде порядок, а не свалка. ViewController перестаёт быть божеством на 1000 строк, и жить становится легче.