0Pricing
Swift Academy · Урок

Связывание моделей представления с представлениями

Соединяйте модели представления со 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 — локальная установка не требуется.

Все уроки этого курса

  1. Шаблон MVVM
  2. Связывание моделей представления с представлениями
  3. Шаблон координатора
  4. Тестирование моделей представления
← Назад к Swift Academy