A Reusable Observer Protocol Written In Swift

Date Published:
Last Modified: by

Introduction

One design pattern that I use a lot is the observer pattern. The observer pattern is used when you have an object that needs to notify a list of objects that state changes have happened. This article discusses a reusable component, in Swift, I developed to speed up my development process.

The Observer Pattern

The Observer design pattern is a pretty common design pattern. It is used in almost all GUI toolkits and is a key concept in model-view-controller architectural pattern. The Observer pattern involves a central object that maintains a list of “Observer” objects. The central object notifies its observers of any state/event changes, usually by calling a method on the Observer.

If you are familiar with Swift then you are aware of how protocols are used to implement the delegate pattern. The delegate pattern is limited to only one observer. The Observer Pattern is just like the delegate pattern, but supports multiple delegates per “Observed” object.

I have developed a reusable Observable protocol that can be used to provide Observer pattern support by simply adding the protocol to a class, struct, or enum. Lets first see it in action.

Example Use

This example, as well as the implementation for the Observer protocol, are contained in my GitHub repository SpazstikPlaygrounds. The playground for this article is the Swift.v3/Observer Protocol playground. You will need at least Xcode version 8 (beta 5) to run this playground.

The first thing that we will need is a protocol that is used by the central observed object to inform the observers of any state changes:

@objc protocol MyProtocol: ObservableProtocol {
    func didSayHello() -> String
}

This example has a simple protocol called MyProtocol. It has only one method. The didSayHello method returns a string. There are two things to note from this snippet. The protocol needs to inherits from the ObservableProtocol. In addition the protocol must be marked with @objc.

The second thing you will need is the central object that is observed. In our example we are using the MyMainClass class:

class MyMainClass: Observable {
    var observers: [MyProtocol] = []

    init() {
        // Do initialization stuff
    }

    func thinking() {
        forEachObserver() { observer in
            print(observer.didSayHello())
        }
    }
}
var c = MyMainClass()

The class must extend itself with the Observable protocol. This protocol requires the class property “observers”:

    var observers: [MyProtocol] = []

This is a var that the Observer protocol and its default methods use to keep track of the registered Observers.

The Observable extends the MyMainClass with three methods: add:observer, remove:observer, forEachObserver. The add and remove methods are used to register and deregister observers, while the forEachObserver method can be used by the MyMainClass to call methods on each Observer objects. This is shown in the thinking method:

    func thinking() {
        forEachObserver() { observer in
            print(observer.didSayHello())
        }
    }

The thinking method calls the didSayHello method on each observer and simply prints the returned string on the console.

As far as the the Observers are concerned, you only need to extend them with the protocol derived from ObservableProtocol:

class MySpyClass: MyProtocol {

    init() {
        c.add(observer: self)
    }

    deinit {
        c.remove(observer: self)
    }

    func didSayHello() -> String {
        return "Hello"
    }

    func hello() {

    }
}
let s = MySpyClass()

Notice that the class registers with the central object in its initialization method as well as de-registers itself in its deinit method. Our example registers two different objects, each of a separate class:

class MySpyClass2: MyProtocol {

    init() {
        c.add(observer: self)
    }

    deinit {
        c.remove(observer: self)
    }

    func didSayHello() -> String {
        return "What's up"
    }

    func hello() {

    }
}
let s2 = MySpyClass2()

Now that we have two objects registered we can call the the thinking method on the central object:

c.thinking()

Which in turns calls the didSayHello method on each Observer object and prints the results to the console:

Hello
What's up

Lets move on to the implementation.

Observer Protocol Implementation

The first thing in our implementation is the definition for the ObservableProtocol protocol:

/// Protocol used as the base protocol type for a observer class
@objc protocol ObservableProtocol: class {}

ObservableProtocol is a empty protocol marked the @objc and limited to classes. This means that observers must be class objects.

The next part of the implementation is the declaration for the Observable protocol:

protocol Observable {
    /// Observer is a generic type used for classes used as observers
    associatedtype Observer: ObservableProtocol

    /// observers is the array of references for the the registered observers
    /// to the class
    var observers: [Observer] { get set }

    /// Call this function to register the observer object.
    ///
    /// - parameter observer: A reference to a object that will get notifications
    mutating func add(observer: Observer)

    /// Call this function to de-register the observer object.
    ///
    /// - parameter observer: A reference to the object to de-register
    mutating func remove(observer: Observer)

    /// This method is call by the Observed object to call the passed block
    /// for each registered observer
    ///
    /// - parameter block: A block that is called for each registered observer
    func forEachObserver(_ block: (Observer) -> Void)
}

This protocol has a associated type:

    associatedtype Observer: ObservableProtocol

The associated type for the Observer pattern is a Observer object that implements a ObservableProtocol protocol. This means that Observers are limited to reference objects because they must adopt a ObservableProtocol which is a class only protocol. You will not be able to use structs or enums as Observers.

The Observable protocol defines the three methods used in our example: add:observer, remove:observer, and forEachObserver.

The last portion of our implementation is the extension of Observable with default implementations of the three methods.

extension Observable {
    /// Call this function to register the observer object.
    ///
    /// - parameter observer: A reference to a object that will get notifications
    mutating func add(observer: Observer) {
        self.observers.append(observer)
    }


    /// Call this function to de-register the observer object.
    ///
    /// - parameter observer: A reference to the object to de-register
    mutating func remove(observer: Observer) {
        for (index, entry) in observers.enumerated() {
            if entry === observer {
                observers.remove(at: index)
                return
            }
        }
    }


    /// This method is call by the Observed object to call the passed block
    /// for each registered observer
    ///
    /// - parameter block: A block that is called for each registered observer
    func forEachObserver(_ block: (Observer) -> Void) {
        observers.forEach { (observer) in
            block(observer)
        }
    }
}

The implementation of each method is straightforward. The only thing to note is the remove:observer methods use of the === operator to determine where in the observers array a reference object is located.

In Summary

I am in the process of moving my applications to iOS 10 and Xcode 8. With Xcode 8 Swift has now matured to version 3.0. With this version Swift has progressed to the point that writing components like that discussed in this article are possible. That being said, I am using Xcode 8 beta 5 for writing this playground.

The one thing that has me scratching my head is the requirement to mark the ObservableProtocol and its derivatives with @objc. Without this mark the Swift compiler is unable to determine what is the associated type should be for the Observable protocol. Remove the @objc marks to see for yourself.

The @objc mark enables the class, protocol, or method to be available for Objective-C code. This support is enabling something behind the vail that gives the compile enough information to then determine what type to use for the associated type. I plan on updating this article when I finally determine the cause.

Rodger Higgins is the founder of Spazstik Software, LLC. He has created StackCalc, The Visual Touch Calculator and SPZTracker.

Recent Articles

A Reusable Observer Protocol Written In Swift

One design pattern that I use a lot is the observer pattern. The observer pattern is used when you have an object that needs to notify a list of objects that state changes have happened. This article discusses a reusable component, in Swift, I developed to speed up my development process.


Read More...
How To: Support User Editable Python Macros In A I Os Application

Last month I published a article on how to use JavascriptCore for extending a iOS application with macro support. While Javascript has many uses, as a way for application customization, it would not be my first choice.

A better choice to me would be a language like Python. Being curious, I wondered what it would take to to use Python. This article discusses what I found.


Read More...
How To Example: Extend A I Os Using Javascript Core As A Macro Engine

JavascriptCore is a framework that offers the ability for a iOS application to interact with javascript code. Primarily used for cross platform code sharing, it can also be used to extend a iOS application with macro capabilities. This framework offers a world of possibilities for extending any iOS application.


Read More...
How To: Custom I Os Activity Tracker View Using Ca Layers

The Apple Watch shipped with a captivating activity tracker. The center piece is a really cool spiral animation scheme showing the amount of activity during the day. This image is also shown on the matching iPhone Activity App. I have always wanted to see what it would take to implement this myself. The examples that I see typically use a custom drawRect override, but I always wanted to see what it would take to do with CAShapeLayers.

Implementing a 0-100% control is straight forward when using CAShapeLayer. But how do you implement a progress indicator that support progress values greater then 100%? This How To discusses a solution that I came up with along with it’s potential limitations.


Read More...
How To Display Custom Content On A External Screen From A I Os Device

Being able to display content on a external screen or device is a great capability to add to a iOS application. Especially how easy it is. This article will show the step required to to do this.


Read More...

Follow us on

Articles by published month

Articles by subject matter

Rails Thor Compass Susy Modernizr Rspec Capybara Bettererrors Railspanel Aws Rack Railscasts Http Aws-elastic-beanstalk Ruby-on-rails Rack-rewrite Http-response-codes Pow-amazon-route-53 Stackcalc Iphone Ios Mobile Application Skeumorphic Dns Web-site Elastic-beanstalk Elastic-ip Elastic-load-balancer Tutorial Howto Javascript Javascriptcore Macro Example Swift Design-patterns Observer Python Macros Alamofire External-screen-support Apple-watch Activity-tracker Office Status S100 Cloud Astronomy Picture Mars Apple Usb-c Leonard-nimoy William-shatner Geotag Gps Spztracker Geotagging Photos Secret Marketing Watch Watch-repair Head-transplants Perception Diabeties Sugar Health Rosette-nebula News Spock Comet-lovejoy

Click here to receive free tips and tutorials!

This web site uses javascript exclusively for automating html elements. Please enable javascript to fully experience the features offered on this site.