Связывание моделей представления с представлениями
Соединяйте модели представления со SwiftUI и Combine.
«Связывание моделей представления с представлениями» — бесплатный урок Swift Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Swift Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Swift Academy содержит 4 уроков всего.
Связывание ViewModel с представлением
ViewModel полезен только тогда, когда представление реагирует на его изменения. В SwiftUI за такую реактивность отвечают основанные на Combine обёртки свойств @Published, @StateObject и @ObservedObject.
Вместе они образуют автоматическую привязку: когда ViewModel изменяется, представление перерисовывается.
ObservableObject
Чтобы SwiftUI мог наблюдать за ViewModel, тот должен соответствовать протоколу ObservableObject. Этот протокол предоставляет издателя objectWillChange, который срабатывает перед любым изменением.
final class CounterViewModel: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}Свойства @Published
Пометка свойства атрибутом @Published автоматически отправляет уведомление об изменении при каждом присваивании ему значения.
Каждое свойство @Published передаёт изменения в издатель objectWillChange объекта, поэтому представление точно знает, когда нужно обновиться.
final class SearchViewModel: ObservableObject {
@Published var query: String = ""
@Published var results: [String] = []
}@StateObject
@StateObject сообщает представлению, что оно должно создать ViewModel и владеть им. SwiftUI создаёт его один раз и сохраняет в памяти при повторных отрисовках этого представления.
Используйте @StateObject там, где ViewModel создаётся впервые.
struct CounterView: View {
@StateObject private var viewModel = CounterViewModel()
var body: some View {
Button("Count: \(viewModel.count)") {
viewModel.increment()
}
}
}@ObservedObject
@ObservedObject используется для ViewModel, который был создан в другом месте и передан в представление. Представление наблюдает за ним, но не управляет временем его жизни.
Используйте его для дочерних представлений, получающих ViewModel от родительского.
struct BadgeView: View {
@ObservedObject var viewModel: CounterViewModel
var body: some View {
Text("Total: \(viewModel.count)")
}
}StateObject и ObservedObject
Ключевое различие заключается во владении:
- @StateObject — представление владеет объектом; он сохраняется при повторных отрисовках;
- @ObservedObject — объект передан извне; представление им не владеет.
Использование @ObservedObject там, где нужен @StateObject, может привести к пересозданию объекта и потере его состояния при каждом обновлении родительского представления.
Передача ViewModel дочернему представлению
Создайте ViewModel один раз с помощью @StateObject в родительском представлении, а затем передайте его дочерним представлениям, которые пометят его как @ObservedObject.
struct ParentView: View {
@StateObject private var viewModel = CounterViewModel()
var body: some View {
VStack {
BadgeView(viewModel: viewModel)
Button("Add") { viewModel.increment() }
}
}
}Двусторонние привязки
Для элементов ввода, например текстовых полей, нужна двусторонняя привязка. SwiftUI создаёт её из свойства @Published с помощью префикса $ у обёртки.
struct SearchView: View {
@StateObject private var viewModel = SearchViewModel()
var body: some View {
TextField("Search", text: $viewModel.query)
}
}Управление состоянием интерфейса
Представление должно получать всё отображаемое из опубликованных свойств. Индикаторы загрузки, баннеры ошибок и содержимое — всё это определяется state ViewModel.
final class FeedViewModel: ObservableObject {
enum State { case loading, loaded([String]), error(String) }
@Published var state: State = .loading
}Реакция на состояние
Представление выбирает нужный интерфейс в зависимости от state ViewModel. Императивного кода показа и скрытия нет — используется только декларативное отображение.
struct FeedView: View {
@StateObject var viewModel = FeedViewModel()
var body: some View {
switch viewModel.state {
case .loading: ProgressView()
case .loaded(let items): List(items, id: \.self) { Text($0) }
case .error(let message): Text(message)
}
}
}Обновление в главном потоке
SwiftUI требует, чтобы опубликованные изменения выполнялись в главном потоке. Если ViewModel обновляет state после асинхронной работы, пометьте его атрибутом @MainActor, чтобы гарантировать это.
@MainActor
final class ProfileViewModel: ObservableObject {
@Published var name: String = ""
func load() async {
name = await fetchName()
}
}Быстрая проверка
Проверьте свои знания о привязках.
Итоги
Вы связали ViewModel с представлениями с помощью привязок SwiftUI:
- ObservableObject и @Published отправляют уведомления об изменениях;
- @StateObject создаёт ViewModel и владеет им;
- @ObservedObject наблюдает за переданным объектом;
- @MainActor сохраняет асинхронные обновления в главном потоке.
Теперь, когда реактивные привязки настроены, представление всегда автоматически отражает актуальный state ViewModel.
Часто задаваемые вопросы
Урок «Связывание моделей представления с представлениями» бесплатный?
Да — полный текст урока «Связывание моделей представления с представлениями» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Swift Academy, подпишись на CoddyKit PRO. Курс Swift Academy содержит 4 уроков всего.
Чему я научусь в уроке «Связывание моделей представления с представлениями»?
Соединяйте модели представления со SwiftUI и Combine. Ты практикуешь Swift Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Swift Academy?
Предыдущий опыт не требуется. Swift Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Связывание моделей представления с представлениями»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Swift Academy?
Да. Каждый урок Swift Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Шаблон MVVM
- Связывание моделей представления с представлениями
- Шаблон координатора
- Тестирование моделей представления