Observer in Swift
Introduction to Observers in Swift
An observer in Swift is a mechanism that allows objects to monitor changes in data and respond accordingly. Observers are used in various scenarios, such as:
- Tracking property changes
- Reacting to notifications
- Building dynamic UI updates
This guide explores Swift Observers with separate examples and outputs.
1. Property Observers in Swift
Swift provides two built-in property observers:
- willSet – Executes before a property’s value changes.
- didSet – Executes after a property’s value changes.
Example 1: Tracking Property Changes with didSet
class User {
var name: String {
didSet {
print("User name changed from \(oldValue) to \(name)")
}
}
init(name: String) {
self.name = name
}
}
let user = User(name: "John")
user.name = "David" // Changing value
Output
oldValue
stores the previous value.
didSet
triggers after the value changes.
Example 2: Using willSet
to Observe Changes Before They Happen
class Account {
var balance: Double = 0.0 {
willSet(newAmount) {
print("Balance will change from \(balance) to \(newAmount)")
}
}
}
let myAccount = Account()
myAccount.balance = 1000.0
Output
willSet(newAmount)
runs before the value changes.
Useful for validations and logging changes.
2. Using NotificationCenter for Observing Events
NotificationCenter
is used for broadcasting messages across different parts of an app.
Example 3: Sending and Receiving Notifications
import Foundation
// Define Notification Name
extension Notification.Name {
static let dataUpdated = Notification.Name("dataUpdated")
}
// Observer Class
class Observer {
init() {
NotificationCenter.default.addObserver(self, selector: #selector(handleUpdate), name: .dataUpdated,
object: nil)
}
@objc func handleUpdate(notification: Notification) {
print("Data
Updated Notification Received!")
}
}
// Sender Class
class DataManager {
func updateData() {
print("Data is
being updated...")
NotificationCenter.default.post(name: .dataUpdated, object:
nil)
}
}
// Usage
let observer = Observer()
let dataManager = DataManager()
dataManager.updateData()
Output
Data Updated Notification Received!
NotificationCenter.default.post(...)
sends notifications.
NotificationCenter.default.addObserver(...)
listens for notifications.
3. Observing Changes with KVO (Key-Value Observing)
KVO (Key-Value Observing) allows you to monitor property changes dynamically.
Example 4: Using KVO to Observe a Property
import Foundation
class Car: NSObject {
@objc dynamic var speed: Int = 0
}
class SpeedObserver: NSObject {
var car: Car
init(car: Car) {
self.car = car
super.init()
car.addObserver(self, forKeyPath:
"speed", options: [.old, .new], context: nil)
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if keyPath == "speed" {
print("Speed changed from \(change?[.oldKey] ?? 0) to
\(change?[.newKey] ?? 0)")
}
}
}
// Usage
let car = Car()
let observer = SpeedObserver(car: car)
car.speed = 60
car.speed = 80
Output
Speed changed from 60 to 80
@objc dynamic var
enables KVO compatibility.
addObserver(...)
registers for property change notifications.
4. Observing Combine Publishers (SwiftUI & Functional Programming)
With Combine framework, observers handle real-time data streams.
Example 5: Using Combine to Observe Data Changes
import Combine
class User {
@Published var age: Int = 0
}
let user = User()
let subscription = user.$age.sink { newAge in
print("User age changed to
\(newAge)")
}
user.age = 25
user.age = 30
Output
User age changed to 30
@Published
automatically publishes changes.
.sink {}
reacts to new values dynamically.
5. Observer Design Pattern (Custom Implementation)
Sometimes, you may need a manual observer pattern for decoupled communication.
Example 6: Creating a Custom Observer Pattern
protocol Observer {
func update(message: String)
}
class Subject {
private var observers: [Observer] = []
func addObserver(_ observer: Observer) {
observers.append(observer)
}
func notifyObservers(message: String) {
for observer in observers {
observer.update(message: message)
}
}
}
// Observer Implementation
class ConcreteObserver: Observer {
func update(message: String) {
print("Observer
received message: \(message)")
}
}
// Usage
let subject = Subject()
let observer1 = ConcreteObserver()
let observer2 = ConcreteObserver()
subject.addObserver(observer1)
subject.addObserver(observer2)
subject.notifyObservers(message: "New event happened!")
Output
Observer received message: New event happened!
notifyObservers()
sends updates to all registered observers.
Useful for event-driven programming.
6. When to Use Observers in Swift?
Use Case | Best Observer Type |
---|---|
Property Change Tracking | willSet / didSet |
Broadcasting Events | NotificationCenter |
Monitoring Dynamic Properties | KVO (Key-Value Observing) |
SwiftUI / Reactive Programming | Combine Framework |
Custom Decoupled Communication | Custom Observer Pattern |
Choose the right observer based on your Swift application needs.
Conclusion
Observers in Swift enhance data flow and event-driven programming. Whether using property observers,
NotificationCenter
, KVO, or Combine, each method provides efficient event tracking.