@Published: что это, принцип работы и применение

Автор: IT Sectr Опубликовано: 2026-06-19 Время чтения: 8 мин

@Published — property wrapper из фреймворка Combine, который автоматически публикует изменения свойства класса, conforming к протоколу ObservableObject. Когда значение свойства, помеченного @Published, изменяется, SwiftUI получает сигнал через objectWillChange и перерисовывает все view, подписанные на этот объект. По данным Apple Combine Framework Documentation (2025), @Published генерирует Publisher, который можно дополнительно трансформировать через операторы Combine: map, filter, debounce и другие. Это делает @Published ключевым мостом между данными и пользовательским интерфейсом в архитектуре MVVM.

Главное

  • @Published — property wrapper для автоматической публикации изменений свойства ObservableObject в SwiftUI и Combine
  • Механизм: при изменении значения вызывается objectWillChange, что триггерит перерисовку подписанных view
  • Publisher доступен через проекцию $property — можно подписываться, комбинировать и трансформировать поток
  • ObservedObject и StateObject автоматически подписываются на @Published свойства — ручная подписка не требуется
  • iOS 17+ макрос @Observable предлагает альтернативу, но @Published остаётся стандартом для Combine-пайплайнов

Что такое @Published?

@Published — это property wrapper, определённый в модуле Combine, который добавляет к свойству класса возможность автоматически уведомлять подписчиков об изменениях. Он может применяться только внутри класса (не в структуре) и только к свойствам класса, conforming к протоколу ObservableObject.

При изменении значения @Published-свойства Combine генерирует событие через встроенный publisher, доступный через префикс доллара: $propertyName. Этот publisher — ObservableObjectPublisher, который принадлежит самому ObservableObject. SwiftUI автоматически подписывается на него, когда view использует @ObservedObject или @StateObject, и перерисовывает view при любом изменении любого @Published свойства внутри объекта.

По данным книги Мэтта Нойбурга «IOS 18 Programming Fundamentals with Swift» (2025), @Published — это удобная обёртка над паттерном willSet, автоматически вызывающая objectWillChange.send(). Фактически компилятор разворачивает @Published в computed property с willSet observer, что даёт нулевой оверхед на runtime по сравнению с ручной реализацией.

Используйте @Published для всех свойств ObservableObject, изменения которых должны отражаться в интерфейсе. Для свойств, не влияющих на UI, обычные stored properties без @Published снижают количество лишних перерисовок.

Как работает @Published

@Published генерирует два ключевых элемента при компиляции. Первый — хранимое свойство с willSet-наблюдателем, который вызывает objectWillChange.send() перед записью нового значения. Второй — проекция $propertyName, возвращающая Published.Publisher, который можно использовать напрямую в Combine-пайплайнах.

Рассмотрим класс Settings с тремя свойствами: два @Published и одно обычное:

swift
class Settings: ObservableObject {
    @Published var username: String = "Guest"
    @Published var isDarkMode = false
    var lastLogin: Date = Date()  // without @Published
}

При изменении username или isDarkMode SwiftUI перерисует все view, подписанные на экземпляр Settings. Изменение lastLogin не вызовет перерисовку. Если нужно вручную уведомить подписчиков об изменении обычного свойства, можно вызвать objectWillChange.send() в willSet-наблюдателе.

Важная деталь: @Published публикует изменения только при прямой записи в свойство. Если свойство является ссылочным типом (класс) и меняется его внутреннее состояние без замены ссылки, @Published этого не заметит. В таких случаях нужна ручная отправка события или замена на value type (структуру).

@Published и Combine

@Published тесно интегрирован с Combine — каждое @Published-свойство автоматически предоставляет publisher, доступный через проекцию $propertyName. Это позволяет применять операторы Combine для фильтрации, трансформации, объединения и отложенной обработки значений.

Типичный сценарий — поиск с debounce. Поле ввода привязано к @Published свойству searchText, но запрос к серверу должен отправляться только после паузы в 300 мс. Combine с $searchText.debounce решает это одной строкой:

swift
class SearchViewModel: ObservableObject {
    @Published var searchText = ""
    @Published var results: [String] = []
    private var cancellables = Set<AnyCancellable>()

    init() {
        setupSearchSubscription()
    }

    private func setupSearchSubscription() {
        $searchText
            .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
            .removeDuplicates()
            .sink { [weak self] text in
                self?.performSearch(text)
            }
            .store(in: &cancellables)
    }

    private func performSearch(_ text: String) { }
}

По данным статьи Джона Сандлса (Swift by Sundell, 2024), комбинирование @Published с Combine — стандартный паттерн для реактивных пайплайнов в SwiftUI-приложениях: validation, debounce, throttle, combineLatest, merge с другими publishers. @Published acts как мост между императивным UI кодом и реактивным Combine.

@Published vs @Observable макрос

С выходом iOS 17 Apple представила макрос @Observable, который предлагает альтернативный подход к реактивности без ObservableObject и @Published. @Observable автоматически отслеживает доступ к свойствам на уровне чтения, а не записи, что даёт более точные перерисовки — обновляется только та view, которая читает конкретное изменённое свойство.

Однако это не означает, что @Published устарел. @Published остаётся необходимым, когда нужна интеграция с Combine-пайплайнами — проекция $propertyName даёт publisher, которого нет у @Observable. Кроме того, для обратной совместимости с iOS 16 и ниже @Published+ObservableObject — единственный вариант. По данным Apple WWDC 2023 сессии «Discover Observation in SwiftUI», Apple рекомендует @Observable для новых проектов, но явно сохраняет поддержку @Published для существующего кода и Combine-сценариев.

На практике многие проекты используют гибридный подход: новые модели данных пишутся на @Observable, а существующие ObservableObject с @Published остаются без рефакторинга. @Published также незаменим, когда требуется тонкий контроль над публикацией — например, отложить уведомление до завершения пакетного обновления нескольких свойств.

Типовые ошибки с @Published

Первая ошибка — применение @Published в структуре. Компилятор выдаст ошибку: «Property wrapper cannot be applied to a computed property» или «'@Published' is only available on members of a class». @Published требует ссылочной семантики, поскольку ObservableObjectPublisher — класс, который должен быть уникальным для каждого экземпляра.

Вторая ошибка — мутация содержимого ссылочного свойства без замены ссылки. Если @Published свойство имеет тип массива [String] и вы вызываете array.append("new"), @Published не уловит изменение, потому что ссылка на массив не изменилась. Решение: присвоить свойству новое значение array = array + ["new"] или использовать objectWillChange.send() вручную.

Третья ошибка — избыточное количество @Published свойств. Каждое @Published свойство вызывает перерисовку всех view, подписанных на ObservableObject, а не только тех, что читают это свойство. По данным Point-Free (2025), разделение одного большого ObservableObject на несколько маленьких с @StateObject и @EnvironmentObject уменьшает количество лишних перерисовок и улучшает производительность.

Примеры кода

Первый пример — ViewModel формы регистрации с валидацией. @Published свойства email и password триггерят отображение ошибок валидации через Combine pipeline:

swift
class RegistrationViewModel: ObservableObject {
    @Published var email = ""
    @Published var password = ""
    @Published var emailError: String?
    @Published var isFormValid = false
    private var cancellables = Set<AnyCancellable>()

    init() {
        $email
            .map { $0.contains("@") ? nil : "Invalid email" }
            .assign(to: &$emailError)
            .store(in: &cancellables)

        $email.combineLatest($password)
            .map { !$0.isEmpty && !$1.isEmpty }
            .assign(to: &$isFormValid)
            .store(in: &cancellables)
    }
}

Второй пример — ручная публикация для коллекции ссылочных элементов. Вместо замены всего массива при каждом изменении внутри элемента используется objectWillChange.send():

swift
class TodoItem {
    var title: String
    var isDone = false
    init(title: String) { self.title = title }
}

class TodoListViewModel: ObservableObject {
    @Published var items: [TodoItem] = []

    func toggle(item: TodoItem) {
        item.isDone.toggle()
        self.objectWillChange.send()  // manual notification
    }
}

Третий пример — Assign к @Published свойству через Combine. Используя новый синтаксис Swift 5.9, можно напрямую присваивать через проекцию assign(to: &$property) без Optional-обёртки. Это самый короткий путь связать publisher с @Published свойством без создания подписки.

Часто задаваемые вопросы

Можно ли использовать @Published в структуре?

Нет, @Published может применяться только внутри класса, conforming к ObservableObject. В структурах используйте @State для локального состояния или @Bindable с макросом @Observable в iOS 17+. Попытка применить @Published в структуре вызовет ошибку компиляции.

Как @Published работает с массивами и словарями?

Правильно: присваивайте новое значение целиком (array = array + ["new"]). @Published отслеживает замену ссылки, а не мутацию содержимого. Для коллекций ссылочных типов используйте ручной вызов objectWillChange.send() после мутации внутреннего состояния элементов.

Чем @Published отличается от @State?

@State предназначен для локального состояния внутри одной view и работает только с value types. @Published — для свойств ObservableObject, которые могут читаться множеством view через @ObservedObject или @EnvironmentObject. @State проще, @Published мощнее благодаря интеграции с Combine.

Нужно ли @Published для каждого свойства ObservableObject?

Только для тех, чьи изменения должны обновлять UI. Свойства для внутренних расчётов, кэши или временные флаги не требуют @Published — это снижает количество лишних перерисовок. Используйте @Published как сигнал «это свойство важно для интерфейса».

Как @Published работает с Core Data?

SwiftUI интегрируется с Core Data через @FetchRequest и @ObservedObject для NSManagedObject. ManagedObject уже conforms к ObservableObject, поэтому @Published не нужен — NSManagedObject сам уведомляет об изменениях. @Published используется в ViewModel-слое между Core Data и UI для трансформации данных.

Итоги

  • @Published — property wrapper из Combine, автоматически публикующий изменения свойств ObservableObject для SwiftUI и Combine-пайплайнов
  • Механизм: willSet observer вызывает objectWillChange.send(), генерируя publisher через проекцию $property
  • Combine: @Published даёт publisher для debounce, map, combineLatest и других операторов — это мост между UI и реактивными пайплайнами
  • @Observable (iOS 17+) — альтернатива для новых проектов, но @Published остаётся стандартом для Combine и обратной совместимости
  • Ошибки: @Published не работает в структурах, не отслеживает мутацию ссылочных типов, избыточное количество @Published увеличивает перерисовки
  • Лучшая практика: помечайте @Published только свойства, влияющие на UI, разделяйте большие ObservableObject на несколько маленьких
  • Assign: assign(to: &$property) в Swift 5.9 позволяет подписать publisher напрямую на @Published свойство

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также