@Published is a property wrapper from the Combine framework that automatically publishes changes to a class property conforming to the ObservableObject protocol. When the value of a property marked with @Published changes, SwiftUI receives a signal through objectWillChange and redraws all views subscribed to that object. According to Apple Combine Framework Documentation (2025), @Published generates a Publisher that can be further transformed using Combine operators: map, filter, debounce, and others. This makes @Published a key bridge between data and the user interface in the MVVM architecture.
Key Takeaways
objectWillChange is called, triggering a redraw of subscribed views$property projection — you can subscribe, combine, and transform the stream@Published is a property wrapper defined in the Combine module that adds the ability to automatically notify subscribers of changes to a class property. It can only be used inside a class (not a struct) and only on properties of a class conforming to the ObservableObject protocol.
When a @Published property value changes, Combine generates an event through a built-in publisher accessible via the dollar prefix: $propertyName. This publisher is an ObservableObjectPublisher, which belongs to the ObservableObject itself. SwiftUI automatically subscribes to it when a view uses @ObservedObject or @StateObject, and redraws the view whenever any @Published property within the object changes.
According to Matt Neuburg’s book “IOS 18 Programming Fundamentals with Swift” (2025), @Published is a convenient wrapper over the willSet pattern, automatically calling objectWillChange.send(). In effect, the compiler expands @Published into a computed property with a willSet observer, providing zero runtime overhead compared to manual implementation.
Use @Published for all ObservableObject properties whose changes should be reflected in the interface. For properties that do not affect the UI, regular stored properties without @Published reduce unnecessary redraws.
@Published generates two key elements at compile time. The first is a stored property with a willSet observer that calls objectWillChange.send() before writing the new value. The second is the $propertyName projection, which returns a Published.Publisher that can be used directly in Combine pipelines.
Consider the Settings class with three properties: two @Published and one regular:
class Settings: ObservableObject {
@Published var username: String = "Guest"
@Published var isDarkMode = false
var lastLogin: Date = Date() // without @Published
}
When username or isDarkMode changes, SwiftUI will redraw all views subscribed to the Settings instance. Changing lastLogin will not trigger a redraw. If you need to manually notify subscribers of a change to a regular property, you can call objectWillChange.send() in the willSet observer.
An important detail: @Published only publishes changes when a property is directly assigned. If the property is a reference type (class) and its internal state changes without replacing the reference, @Published will not detect it. In such cases, manual event sending or switching to a value type (struct) is required.
@Published is tightly integrated with Combine — each @Published property automatically provides a publisher accessible via the $propertyName projection. This allows using Combine operators for filtering, transforming, combining, and deferred value processing.
A typical scenario is search with debounce. An input field is bound to the @Published property searchText, but the server request should only be sent after a 300 ms pause. Combine with $searchText.debounce solves this in one line:
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) { }
}
According to John Sundell’s article (Swift by Sundell, 2024), combining @Published with Combine is a standard pattern for reactive pipelines in SwiftUI applications: validation, debounce, throttle, combineLatest, merge with other publishers. @Published acts as a bridge between imperative UI code and reactive Combine.
With the release of iOS 17, Apple introduced the @Observable macro, which offers an alternative approach to reactivity without ObservableObject and @Published. @Observable automatically tracks property access at the read level rather than write level, providing more precise redraws — only the view that reads a specific changed property gets updated.
However, this does not mean @Published is deprecated. @Published remains necessary when Combine pipeline integration is needed — the $propertyName projection provides a publisher that @Observable does not have. Additionally, for backward compatibility with iOS 16 and below, @Published+ObservableObject is the only option. According to Apple WWDC 2023 session “Discover Observation in SwiftUI,” Apple recommends @Observable for new projects but explicitly maintains support for @Published for existing code and Combine scenarios.
In practice, many projects use a hybrid approach: new data models use @Observable, while existing ObservableObject with @Published remain without refactoring. @Published is also indispensable when fine-grained control over publication is required — for example, delaying notification until a batch update of multiple properties is complete.
The first mistake is using @Published in a struct. The compiler will throw an error: “Property wrapper cannot be applied to a computed property” or “‘@Published’ is only available on members of a class.” @Published requires reference semantics because ObservableObjectPublisher is a class that must be unique for each instance.
The second mistake is mutating the contents of a reference property without replacing the reference. If a @Published property is of array type [String] and you call array.append("new"), @Published will not detect the change because the reference to the array has not changed. Solution: assign a new value to the property array = array + ["new"] or use objectWillChange.send() manually.
The third mistake is an excessive number of @Published properties. Each @Published property triggers a redraw of all views subscribed to the ObservableObject, not just those that read that property. According to Point-Free (2025), splitting one large ObservableObject into several smaller ones with @StateObject and @EnvironmentObject reduces unnecessary redraws and improves performance.
The first example is a registration form ViewModel with validation. The @Published properties email and password trigger validation error display through a 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)
}
}
The second example is manual publication for a collection of reference elements. Instead of replacing the entire array on every change within an element, objectWillChange.send() is used:
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
}
}
The third example is Assign to a @Published property through Combine. Using the new Swift 5.9 syntax, you can directly assign via the assign(to: &$property) projection without an Optional wrapper. This is the shortest way to connect a publisher to a @Published property without creating a subscription.
Frequently Asked Questions
No, @Published can only be used inside a class conforming to ObservableObject. In structs, use @State for local state or @Bindable with the @Observable macro in iOS 17+. Attempting to use @Published in a struct will cause a compilation error.
Correct approach: assign the new value entirely (array = array + ["new"]). @Published tracks reference replacement, not content mutation. For collections of reference types, use manual objectWillChange.send() after mutating the internal state of elements.
@State is designed for local state within a single view and only works with value types. @Published is for ObservableObject properties that can be read by multiple views via @ObservedObject or @EnvironmentObject. @State is simpler, @Published is more powerful thanks to Combine integration.
Only those whose changes should update the UI. Properties for internal calculations, caches, or temporary flags do not need @Published — this reduces unnecessary redraws. Use @Published as a signal that “this property matters for the interface.”
SwiftUI integrates with Core Data through @FetchRequest and @ObservedObject for NSManagedObject. ManagedObject already conforms to ObservableObject, so @Published is not needed — NSManagedObject notifies changes on its own. @Published is used in the ViewModel layer between Core Data and UI for data transformation.
Summary
objectWillChange.send(), generating a publisher via the $property projectionassign(to: &$property) in Swift 5.9 allows subscribing a publisher directly to a @Published propertyWe will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also