With iOS 26, Apple brought one of the most beloved features of SwiftUI to UIKit—the long-awaited ability to observe state directly in the UI layer. Combined with the new updateProperties() lifecycle method, UIKit developers can now enjoy the reactive programming benefits that SwiftUI developers have been raving about for years. updateProperties() Before iOS 26 Prior to iOS 26, updating the UI in UIKit typically relied on methods like the ones shown below. final class ViewController: UIViewController { private var data: UserData? { didSet { updateUI() } } private func updateUI() { usernameLabel.text = data?.username userImageView.image = data?.image setNeedsLayout() } } final class ViewController: UIViewController { private var data: UserData? { didSet { updateUI() } } private func updateUI() { usernameLabel.text = data?.username userImageView.image = data?.image setNeedsLayout() } } If the updateUI method was not called, the user interface would not update accordingly. On the other hand, calling it too frequently could negatively impact performance. Fortunately, this long-standing dilemma has finally been resolved. updateUI Introducing Automatic Observation Tracking iOS 26 introduces Swift's @Observable macro to UIKit, offering full automatic tracking capabilities. When observable properties are accessed within designated UIKit methods, the framework automatically establishes dependencies and ensures that views are invalidated and updated when those properties change. @Observable @Observable class DataModel { var image: UIImage var username: String } final class UserViewController: UIViewController { var dataModel: DataModel var usernameLabel: UILabel var userImage: UIImageView override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() // UIKit automatically tracks these properties userImage.image = dataModel.image usernameLabel.text = dataModel.username } } @Observable class DataModel { var image: UIImage var username: String } final class UserViewController: UIViewController { var dataModel: DataModel var usernameLabel: UILabel var userImage: UIImageView override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() // UIKit automatically tracks these properties userImage.image = dataModel.image usernameLabel.text = dataModel.username } } There is no longer a need to call setNeedsLayout() or manually trigger update methods. By simply applying the @Observable macro to your data class and implementing the viewWillLayoutSubviews method, UIKit will: setNeedsLayout() @Observable viewWillLayoutSubviews Automatically track dependencies such as image and username during the initial layout,Invalidate the view when these properties change, andReinvoke viewWillLayoutSubviews() to ensure the UI remains in sync. Automatically track dependencies such as image and username during the initial layout, image username Invalidate the view when these properties change, and Reinvoke viewWillLayoutSubviews() to ensure the UI remains in sync. viewWillLayoutSubviews() Makes CollectionView Usage Easier @Observable class ItemModel { var title: String var subtitle: String } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) let itemModel = itemModel(for: indexPath) cell.configurationUpdateHandler = { cell, state in var content = UIListContentConfiguration.subtitleCell() content.text = listItemModel.title content.secondaryText = listItemModel.subtitle cell.contentConfiguration = content } return cell } @Observable class ItemModel { var title: String var subtitle: String } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) let itemModel = itemModel(for: indexPath) cell.configurationUpdateHandler = { cell, state in var content = UIListContentConfiguration.subtitleCell() content.text = listItemModel.title content.secondaryText = listItemModel.subtitle cell.contentConfiguration = content } return cell } Ready to modernize your UIKit apps? To take full advantage of the new reactive capabilities introduced in iOS 26, begin by updating your project to the iOS 26 SDK and recompiling your existing applications. Next, identify areas in your codebase that rely on manual UI update patterns. Convert suitable data models to use the @Observable macro, and implement the updateProperties() method to handle property-specific update logic. For enhanced UI responsiveness and smoother animations, utilize flushUpdates() where appropriate. @Observable updateProperties() flushUpdates() UIKit is entering a new era—more reactive, performant, and developer-friendly than ever before. Welcome to the future of iOS development.