Understanding @Published in SwiftUI: Risks and Best Practices

KD Knowledge Diet
3 min readAug 19, 2024

--

Hello SwiftUI enthusiasts! 🚀 In this article, we’ll demystify the @Published property wrapper, a powerful tool in SwiftUI for updating views when data changes. We’ll dive into how it works, explore potential pitfalls, and provide you with best practices. Let’s get started!

What’s the @Published Property Wrapper?

The @Published property wrapper is your go-to choice when you want to trigger view updates in SwiftUI whenever a property changes. You can use it within classes that conform to the `ObservableObject` protocol or in regular classes.

Here’s how you use it:

final class ArticleViewModel {
@Published
var title: String = "An example title"
}

Important note: The @Published property wrapper is designed for class instances and won’t work with structs.

The wrapped property, in this case, `title`, represents the actual value of the property. You can also access a publisher using `$title`, which allows you to observe changes:

var cancellable = viewModel.$title.sink(receiveValue: { newTitle in
print("Title changed to \(newTitle)")
})
viewModel.title = "@Published explained"

Understanding the willSet Trigger

To grasp how @Published works, it’s crucial to understand that changes are published in the property’s `willSet` block. This means that subscribers receive updates before the property’s value is actually changed. Let’s illustrate this behavior:

var cancellable = viewModel.$title.sink(receiveValue: { newTitle in
print("Title changed to: '\(newTitle)'")
print("ViewModel title is: '\(viewModel.title)'")
})
viewModel.title = "@Published explained"

The output might surprise you:

Title changed to: '@Published explained'
ViewModel title is: 'An example title'

The subscriber correctly receives the new title, but when you access the property within the `willSet` block, it still returns the previous value.

Risks and Pitfalls

Understanding this `willSet` behavior is crucial because it can lead to unexpected behavior and bugs. One common issue arises when trying to update a label’s text using a generic function:

override func viewDidLoad() {
super.viewDidLoad()
cancellable = viewModel.$title.sink(receiveValue: { [weak self] newTitle in
// Update the title label.
self?.updateTitleLabel()
})
// Initial update of the title label.
updateTitleLabel()
}
func updateTitleLabel() {
titleLabel.text = viewModel.title
print("Title label text is now: '\(titleLabel.text ?? "empty")'")
}

Since we’re calling `updateTitleLabel()` inside the `sink` operator, it triggers during the `willSet`, resulting in the previous value being displayed. To fix this, always use the published value as the source of truth:

override func viewDidLoad() {
super.viewDidLoad()
cancellable = viewModel.$title.sink(receiveValue: { [weak self] newTitle in
self?.titleLabel.text = newTitle
print("Title label text is now: '\(self?.titleLabel.text ?? "empty")'")
})
}

Why Use willSet Instead of didSet?

You might wonder why SwiftUI uses `willSet` rather than `didSet`. The reason is that SwiftUI needs to perform a diffing operation between the old state and the new state to determine whether to render the view. For this, it requires access to the old state, which `willSet` provides.

Leveraging @Published and @ObservableObject

While we’ve discussed @Published in a regular class, it’s more common to use it within an observed object:

final class ArticleViewModel: ObservableObject {
@Published
var title: String = "An example title"
}

The `ObservableObject` protocol automatically synthesizes an `objectWillChange` publisher, which emits when any of its contained @Published properties change. SwiftUI uses this publisher to trigger view updates.

Conclusion

The @Published property wrapper is a powerful tool for responding to property changes in SwiftUI. Just remember that it triggers changes in the `willSet` block, so always rely on the published value for consistency. When combined with the `ObservableObject` protocol, it forms a robust foundation for SwiftUI apps.

If you’re eager to enhance your SwiftUI skills, check out the SwiftUI category page for more valuable insights and tips. Feel free to reach out or tweet me on Twitter if you have any questions or feedback. Happy SwiftUI coding! 🧑‍💻

--

--

KD Knowledge Diet
KD Knowledge Diet

Written by KD Knowledge Diet

Software Engineer, Mobile Developer living in Seoul. I hate people using difficult words. Why not using simple words? Keep It Simple Stupid!

No responses yet