@Published — це property wrapper із фреймворку Combine, який автоматично публікує зміни властивості класу, що відповідає протоколу ObservableObject. Коли значення властивості, позначеної @Published, змінюється, SwiftUI отримує сигнал через objectWillChange і перемальовує всі view, підписані на цей об'єкт. Згідно з документацією Apple Combine Framework (2025), @Published генерує Publisher, який можна додатково трансформувати через оператори Combine: map, filter, debounce та інші. Це робить @Published ключовим мостом між даними та інтерфейсом користувача в архітектурі MVVM.
Головне
objectWillChange, що тригерить перемальовування підписаних view$property — можна підписуватися, комбінувати та трансформувати потік@Published — це property wrapper, визначений у модулі Combine, який додає можливість автоматично сповіщати підписників про зміни властивості класу. Він може застосовуватися лише всередині класу (не в структурі) і лише до властивостей класу, що відповідає протоколу 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, що дає нульовий overhead на runtime порівняно з ручною реалізацією.
Використовуйте @Published для всіх властивостей ObservableObject, зміни яких повинні відображатися в інтерфейсі. Для властивостей, що не впливають на UI, звичайні stored properties без @Published зменшують кількість зайвих перемальовувань.
@Published генерує два ключові елементи при компіляції. Перший — властивість, що зберігається, з willSet-спостерігачем, який викликає objectWillChange.send() перед записом нового значення. Другий — проекція $propertyName, яка повертає Published.Publisher, що можна використовувати безпосередньо в Combine-пайплайнах.
Розглянемо клас Settings із трьома властивостями: дві @Published і одна звичайна:
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 автоматично надає publisher, доступний через проекцію $propertyName. Це дозволяє застосовувати оператори Combine для фільтрації, трансформації, об'єднання та відкладеної обробки значень.
Типовий сценарій — пошук із debounce. Поле введення прив'язане до властивості @Published searchText, але запит до сервера має надсилатися лише після паузи в 300 мс. Combine із $searchText.debounce вирішує це одним рядком:
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 виступає як міст між імперативним UI кодом і реактивним Combine.
З виходом 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 у структурі. Компілятор видасть помилку: «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:
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():
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 може застосовуватися лише всередині класу, що відповідає ObservableObject. У структурах використовуйте @State для локального стану або @Bindable з макросом @Observable в iOS 17+. Спроба застосувати @Published у структурі викличе помилку компіляції.
Правильно: присвоюйте нове значення цілком (array = array + ["new"]). @Published відстежує заміну посилання, а не мутацію вмісту. Для колекцій посилальних типів використовуйте ручний виклик objectWillChange.send() після мутації внутрішнього стану елементів.
@State призначений для локального стану всередині одного view і працює лише з value types. @Published — для властивостей ObservableObject, які можуть читатися багатьма view через @ObservedObject або @EnvironmentObject. @State простіший, @Published потужніший завдяки інтеграції з Combine.
Лише для тих, чиї зміни повинні оновлювати UI. Властивості для внутрішніх розрахунків, кешу або тимчасових прапорців не потребують @Published — це зменшує кількість зайвих перемальовувань. Використовуйте @Published як сигнал «ця властивість важлива для інтерфейсу».
SwiftUI інтегрується з Core Data через @FetchRequest і @ObservedObject для NSManagedObject. ManagedObject вже відповідає ObservableObject, тому @Published не потрібен — NSManagedObject сам сповіщає про зміни. @Published використовується в ViewModel-шарі між Core Data та UI для трансформації даних.
Підсумки
objectWillChange.send(), генеруючи publisher через проекцію $propertyassign(to: &$property) у Swift 5.9 дозволяє підписати publisher безпосередньо на властивість @PublishedМи розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також