@Published — to property wrapper z frameworka Combine, który automatycznie publikuje zmiany właściwości klasy zgodnej z protokołem ObservableObject. Gdy wartość właściwości oznaczonej @Published zmienia się, SwiftUI otrzymuje sygnał przez objectWillChange i przerysowuje wszystkie widoki subskrybujące ten obiekt. Według Apple Combine Framework Documentation (2025), @Published generuje Publisher, który można dodatkowo transformować za pomocą operatorów Combine: map, filter, debounce i innych. To czyni @Published kluczowym mostem między danymi a interfejsem użytkownika w architekturze MVVM.
Najważniejsze
objectWillChange, co wyzwala przerysowanie subskrybujących widoków$property — można subskrybować, łączyć i transformować strumień@Published — to property wrapper zdefiniowany w module Combine, który dodaje do właściwości klasy możliwość automatycznego powiadamiania subskrybentów o zmianach. Może być stosowany tylko wewnątrz klasy (nie w strukturze) i tylko do właściwości klasy zgodnej z protokołem ObservableObject.
Przy zmianie wartości właściwości @Published, Combine generuje zdarzenie przez wbudowany publisher, dostępny przez prefiks dolara: $propertyName. Ten publisher to ObservableObjectPublisher, który należy do samego ObservableObject. SwiftUI automatycznie subskrybuje go, gdy widok używa @ObservedObject lub @StateObject, i przerysowuje widok przy każdej zmianie dowolnej właściwości @Published wewnątrz obiektu.
Według książki Matta Neuburga „IOS 18 Programming Fundamentals with Swift” (2025), @Published to wygodne opakowanie wzorca willSet, automatycznie wywołujące objectWillChange.send(). Faktycznie kompilator rozwija @Published we właściwość obliczaną z obserwatorem willSet, co daje zerowy narzut w czasie działania w porównaniu z ręczną implementacją.
Używaj @Published dla wszystkich właściwości ObservableObject, których zmiany mają być odzwierciedlone w interfejsie. Dla właściwości niewpływających na UI zwykłe stored properties bez @Published zmniejszają liczbę niepotrzebnych przerysowań.
@Published generuje dwa kluczowe elementy podczas kompilacji. Pierwszy — przechowywana właściwość z obserwatorem willSet, który wywołuje objectWillChange.send() przed zapisaniem nowej wartości. Drugi — projekcja $propertyName, zwracająca Published.Publisher, który można użyć bezpośrednio w pipeline’ach Combine.
Rozważmy klasę Settings z trzema właściwościami: dwiema @Published i jedną zwykłą:
class Settings: ObservableObject {
@Published var username: String = "Guest"
@Published var isDarkMode = false
var lastLogin: Date = Date() // bez @Published
}
Przy zmianie username lub isDarkMode SwiftUI przerysuje wszystkie widoki subskrybujące instancję Settings. Zmiana lastLogin nie wywoła przerysowania. Jeśli potrzebujesz ręcznie powiadomić subskrybentów o zmianie zwykłej właściwości, możesz wywołać objectWillChange.send() w obserwatorze willSet.
Ważny szczegół: @Published publikuje zmiany tylko przy bezpośrednim przypisaniu do właściwości. Jeśli właściwość jest typu referencyjnego (klasa) i zmienia się jej stan wewnętrzny bez zastąpienia referencji, @Published tego nie wychwyci. W takich przypadkach potrzebne jest ręczne wysłanie zdarzenia lub zastąpienie typem wartościowym (strukturą).
@Published jest ściśle zintegrowany z Combine — każda właściwość @Published automatycznie udostępnia publishera dostępnego przez projekcję $propertyName. Umożliwia to stosowanie operatorów Combine do filtrowania, transformacji, łączenia i opóźniania wartości.
Typowy scenariusz — wyszukiwanie z debounce. Pole wejściowe jest powiązane z właściwością @Published searchText, ale zapytanie do serwera powinno być wysyłane dopiero po pauzie 300 ms. Combine z $searchText.debounce rozwiązuje to w jednej linii:
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) { }
}
Według artykułu Johna Sundella (Swift by Sundell, 2024), łączenie @Published z Combine to standardowy wzorzec dla reaktywnych pipeline’ów w aplikacjach SwiftUI: validation, debounce, throttle, combineLatest, merge z innymi publisherami. @Published działa jako most między imperatywnym kodem UI a reaktywnym Combine.
Wraz z wydaniem iOS 17 Apple przedstawiła makro @Observable, które oferuje alternatywne podejście do reaktywności bez ObservableObject i @Published. @Observable automatycznie śledzi dostęp do właściwości na poziomie odczytu, a nie zapisu, co daje bardziej precyzyjne przerysowania — aktualizowany jest tylko ten widok, który odczytuje konkretną zmienioną właściwość.
Nie oznacza to jednak, że @Published jest przestarzały. @Published pozostaje niezbędny, gdy potrzebna jest integracja z pipeline’ami Combine — projekcja $propertyName daje publishera, którego nie ma w @Observable. Ponadto dla wstecznej zgodności z iOS 16 i starszymi @Published+ObservableObject to jedyna opcja. Według Apple WWDC 2023 sesji „Discover Observation in SwiftUI”, Apple zaleca @Observable dla nowych projektów, ale wyraźnie utrzymuje wsparcie @Published dla istniejącego kodu i scenariuszy Combine.
W praktyce wiele projektów stosuje podejście hybrydowe: nowe modele danych są pisane z @Observable, a istniejące ObservableObject z @Published pozostają bez refaktoryzacji. @Published jest również niezastąpiony, gdy wymagana jest precyzyjna kontrola nad publikacją — na przykład opóźnienie powiadomienia do zakończenia zbiorczej aktualizacji kilku właściwości.
Pierwszy błąd — stosowanie @Published w strukturze. Kompilator zgłosi błąd: „Property wrapper cannot be applied to a computed property” lub „'@Published' is only available on members of a class”. @Published wymaga semantyki referencyjnej, ponieważ ObservableObjectPublisher to klasa, która musi być unikalna dla każdej instancji.
Drugi błąd — mutacja zawartości właściwości referencyjnej bez zastąpienia referencji. Jeśli właściwość @Published ma typ tablicy [String] i wywołujesz array.append("new"), @Published nie wychwyci zmiany, ponieważ referencja do tablicy się nie zmieniła. Rozwiązanie: przypisz właściwości nową wartość array = array + ["new"] lub użyj objectWillChange.send() ręcznie.
Trzeci błąd — nadmierna liczba właściwości @Published. Każda właściwość @Published wywołuje przerysowanie wszystkich widoków subskrybujących ObservableObject, a nie tylko tych, które odczytują tę właściwość. Według Point-Free (2025), podział jednego dużego ObservableObject na kilka małych z @StateObject i @EnvironmentObject zmniejsza liczbę niepotrzebnych przerysowań i poprawia wydajność.
Pierwszy przykład — ViewModel formularza rejestracji z walidacją. Właściwości @Published email i password wyzwalają wyświetlanie błędów walidacji przez pipeline 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)
}
}
Drugi przykład — ręczna publikacja dla kolekcji elementów referencyjnych. Zamiast zastępować całą tablicę przy każdej zmianie wewnątrz elementu używane jest 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() // ręczne powiadomienie
}
}
Trzeci przykład — Assign do właściwości @Published przez Combine. Używając nowej składni Swift 5.9, można bezpośrednio przypisywać przez projekcję assign(to: &$property) bez opakowania Optional. To najkrótsza droga do powiązania publishera z właściwością @Published bez tworzenia subskrypcji.
Często zadawane pytania
Nie, @Published może być stosowany tylko wewnątrz klasy zgodnej z ObservableObject. W strukturach używaj @State dla lokalnego stanu lub @Bindable z makrem @Observable w iOS 17+. Próba zastosowania @Published w strukturze spowoduje błąd kompilacji.
Poprawnie: przypisuj nową wartość w całości (array = array + ["new"]). @Published śledzi zastąpienie referencji, a nie mutację zawartości. Dla kolekcji typów referencyjnych używaj ręcznego wywołania objectWillChange.send() po mutacji stanu wewnętrznego elementów.
@State jest przeznaczony dla lokalnego stanu w obrębie jednego widoku i działa tylko z typami wartościowymi. @Published — dla właściwości ObservableObject, które mogą być odczytywane przez wiele widoków przez @ObservedObject lub @EnvironmentObject. @State jest prostszy, @Published potężniejszy dzięki integracji z Combine.
Tylko dla tych, których zmiany mają aktualizować UI. Właściwości do obliczeń wewnętrznych, pamięci podręcznej lub tymczasowe flagi nie wymagają @Published — to zmniejsza liczbę niepotrzebnych przerysowań. Używaj @Published jako sygnału „ta właściwość jest ważna dla interfejsu”.
SwiftUI integruje się z Core Data przez @FetchRequest i @ObservedObject dla NSManagedObject. ManagedObject jest już zgodny z ObservableObject, więc @Published nie jest potrzebny — NSManagedObject sam powiadamia o zmianach. @Published jest używany w warstwie ViewModel między Core Data a UI do transformacji danych.
Podsumowanie
objectWillChange.send(), generując publisher przez projekcję $propertyassign(to: &$property) w Swift 5.9 umożliwia subskrypcję publishera bezpośrednio do właściwości @PublishedOpracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również