Observer pattern in iOS-Swift ( Behavioral Design Pattern )
In this story, I’m going to write about the Observer design pattern.
Currently, most of the developers use RXSwift or Combine to implement this design pattern, and sometimes we didn’t think about what is going to happen behind these tools. I’m going to explain Observer patterns and write a sample code without RXSwift or Combine to demonstrate the pattern idea.
The Observer design pattern allows a group of objects, known as observers, to get notified about the state changing of another object, refer to as the Subject.
The observers register themselves with the subject. The subject updates all registered observers whenever its state changes.
When an observer gets notified, it can retrieve the subject state it’s interested in.
This approach avoids the tight coupling between the observers and the subject.
The subject only knows about the common protocol. No further implementation details are shared with the subject.
Make sure that the observers get unregistered from the subject before releasing them. Otherwise, the subject keeps the reference to the observer instance that should have been destroyed and continues updating it, leading to bugs and unexpected behavior.
Let’s start to implement Observer pattern.
First, I create the observer protocol. Types will need to conform to this protocol if they want to be notified when the subject changes. and the protocol declares a single method, notify.
protocol Observer {
func notify()
var uid: Int { get }
}
I also add a property called UID. This lets us compare objects that implement the protocol when managing the observers.
Then we need the subject protocol. I define the method register that allows observers to subscribe for notifications from the subject.
protocol Subject {
mutating func register(_ observer: Observer)
mutating func unregister(_ observer: Observer)
}
I declare it as mutating since we want to adopt it, also, in structures. And it has a single parameter, the observer. And I also implement an unregister method. That will let observers unsubscribe.
That’s it, now let's see it in action.
I’ll make the UI label conform to the observer protocol, using an extension.
extension UILabel: Observer {
func notify() {
self.text = "Subject state changed"
}
var uid: Int {
return ObjectIdentifier(self).hashValue
}
}
The notify method just sets the label stats to Subject state changed.
Next, I’ll make the UI Button conform to the subject protocol, using an extension.
extension UIButton: Subject {
private static var observers = [Observer]()
func register(_ observer: Observer) {
UIButton.observers.append(observer)
}
func unregister(_ observer: Observer) {
UIButton.observers = UIButton.observers.filter{$0.uid != observer.uid}
}
}
First, I create a static property called observers. It should be private. It can store an array of objects that conform to the observer protocol.
The register method should append the input parameter to the list of observers.
And for the unregister method, I will rely on the filter array method, which returns the elements of the observers list that satisfy the given predicate.
UIButton now fully conforms to the subject protocol.
func onStateChanged() {
UIButton.observers.forEach { (observer) in
observer.notify()
}
}
I define another method. OnStateChanged is not required to conform to the subject protocol. But we’ll use it to notify the observers. For each observer, I’m going to call the observer's notify method.
Now, let’s put it all together.
protocol Observer {
func notify()
var uid: Int { get }
}
protocol Subject {
mutating func register(_ observer: Observer)
mutating func unregister(_ observer: Observer)
}
extension UILabel: Observer {
func notify() {
self.text = "Subject state changed"
}
var uid: Int {
return ObjectIdentifier(self).hashValue
}
}
extension UIButton: Subject {
private static var observers = [Observer]()
func register(_ observer: Observer) {
UIButton.observers.append(observer)
}
func unregister(_ observer: Observer) {
UIButton.observers = UIButton.observers.filter{$0.uid != observer.uid}
}
func onStateChanged() {
UIButton.observers.forEach { (observer) in
observer.notify()
}
}
}
I also use these extensions in a simple ViewController, which you can find it in this link on Github.
Hopefully, you will find it useful and you can use it in your project. I’d appreciate feedback on it.