Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observer Pattern in Swift

I want to implement an observer pattern, but I do not find the proper programming language constructs in Swift (also 2.0). The main problems are:

  1. protocol and extension does not allow stored properties.
  2. In classes we could add stored properties, but we can not force a subclass to override some of its inherited methods.

This is what I want:

{class|protocol|extension|whathaveyou} Sensor {
    var observers = Array<Any>() // This is not possible in protocol and extensions 
    // The following is does not work in classes
    func switchOn() 
    func switchOff()
    var isRunning : Bool {
        get
    }
}

class LightSensor : Sensor {
    //...
    override func switchOn() {
        // turn the sensor on
    }
}

// In the class C, implementing the protocol 'ObserverProtocol'

var lightSensor = LightSensor()
lightSensor.switchOn()
lightSensor.registerObserver(self) // This is what I want

And here comes what is possible to my knowledge:

class Sensor {
    private var observers = Array<Observer>()

    func registerObserver(observer:ObserverDelegate) {
        observers.append(observer)
    }
}

protocol SensorProtocol {
    func switchOn()
    func switchOff()
    var isRunning : Bool {
        get
    }
}

class LightSensor : Sensor, SensorProtocol {
    func switchOn() {
        //
    }
    func switchOff() {
        //
    }

    var isRunning : Bool {
        get {
            return // whatever
        }
    }
}

But this is not very convenient, because both Sensor and SensorProtocol should come hand in hand, and are both requirements the subclass LightSensor has to fulfill.

Any ideas?

like image 887
Michael Dorner Avatar asked Jun 22 '15 09:06

Michael Dorner


1 Answers

A protocol is an abstract set of requirements shared across a number of (potentially very different) other objects. As such, it's illogical to store data in a protocol. That would be like global state. I can see that you want to define the specification for how the observers are stored though. That would also allow 'you' to remove 'someone else' from being an observer, and it's very restrictive about how the observers are stored.

So, your protocol should expose methods to add and remove 'yourself' as an observer. It's then the responsibility of the object implementing the protocol to decide how and where the observers are stored and to implement the addition and removal.


You could create a struct to work with your protocols, something like:

protocol Observer: class {
    func notify(target: Any)
}

protocol Observable {
    mutating func addObserver(observer: Observer)
    mutating func removeObserver(observer: Observer)
}

struct Observation: Observable {
    var observers = [Observer]()

    mutating func addObserver(observer: Observer) {
        print("adding")
        observers.append(observer)
    }
    mutating func removeObserver(observer: Observer) {
        print("removing")
        for i in observers.indices {
            if observers[i] === observer {
                observers.removeAtIndex(i)
                break
            }
        }
    }
    func notify(target: Any) {
        print("notifying")
        for observer in observers {
            observer.notify(target)
        }
    }
}

struct ATarget: Observable {
    var observation = Observation()

    mutating func addObserver(observer: Observer) {
        observation.addObserver(observer)
    }
    mutating func removeObserver(observer: Observer) {
        observation.removeObserver(observer)
    }

    func notifyObservers() {
        observation.notify(self)
    }
}

class AnObserver: Observer {
    func notify(target: Any) {
        print("notified!")
    }
}

let myObserver = AnObserver()
var myTarget: Observable = ATarget()
myTarget.addObserver(myObserver)

if let myTarget = myTarget as? ATarget {
    myTarget.notifyObservers()
}
like image 137
Wain Avatar answered Sep 27 '22 18:09

Wain