@Published — е property wrapper от фреймърка Combine, който автоматично публикува промените на свойство на клас който е съвместим с протокола ObservableObject. Когато стойността на свойство, маркирано с @Published, се промени, SwiftUI получава сигнал чрез objectWillChange и прерисува всички view, абонирани към този обект. Според Apple Combine Framework Documentation (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 в изчисляемо свойство с наблюдател willSet, което предоставя нулева надглава при изпълнение в сравнение с ръчно изпълнение.
Използвайте @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() // без @Published
}
При промяна на username или isDarkMode, SwiftUI прерисува всички view, абонирани към инстанцията Settings. Промяната на lastLogin няма да предизвика прерисуване. Ако трябва ръчно да уведомите абонатите за промяна на обикновено свойство, можете да викате objectWillChange.send() в наблюдателя willSet.
Важен детайл: @Published публикува промени само при директно записване на свойството. Ако свойството е от референтен тип (клас) и неговото вътрешно състояние се променя без замяна на референцията, @Published няма да го открие. В такива случаи е необходимо ръчно изпращане на събитието или замяна с тип стойност (структура).
@Published е тесно интегриран с Combine — всяко @Published свойство автоматично предоставя publisher, достъпен чрез проекцията $propertyName. Това позволява прилагането на Combine оператори за филтриране, трансформация, комбиниране и забавена обработка на стойности.
Типичен сценарий — търсене с debounce. Полето за въвеждане е свързано с @Published свойството searchText, но заявката към сървъра трябва да се изпрати едва след пауза от 300 ms. 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 пиплайн:
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() // ръчно уведомление
}
}
Третият пример — 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 и работи само с типове стойности. @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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също