I'm assembling a class which has several states, as defined by an enum, and a read-only property "state" which returns the instance's current state. I was hoping to use KVO techniques to observe changes in state but this doesn't seem possible:
dynamic var state:ItemState // Generates compile-time error: Property cannot be marked dynamic because its type cannot be represented in Objective-C
I guess I could represent each state as an Int or String, etc. but is there a simple alternative workaround that would preserve the type safety that the enum would otherwise provide?
Vince.
I came across the same problem a while ago. In the end I used an enum for the state and added an additional 'raw' property which is set by a property observer on the main state property.
You can KVO the 'raw' property but then reference the real enum property when it changes.
It's obviously a bit of a hack but for me it was better than ditching the enum altogether and losing all the benefits.
eg.
class Model : NSObject {
enum AnEnumType : String {
case STATE_A = "A"
case STATE_B = "B"
}
dynamic private(set) var enumTypeStateRaw : String?
var enumTypeState : AnEnumType? {
didSet {
enumTypeStateRaw = enumTypeState?.rawValue
}
}
}
ADDITIONAL:
If you are writing the classes that are doing the observing in Swift here's a handy utility class to take some of the pain away. The benefits are:
The utility class is called KVOObserver
and an example usage is:
class ExampleObserver {
let model : Model
private var modelStateKvoObserver : KVOObserver?
init(model : Model) {
self.model = model
modelStateKvoObserver = KVOObserver.observe(model, keyPath: "enumTypeStateRaw") { [unowned self] in
println("new state = \(self.model.enumTypeState)")
}
}
}
Note [unowned self]
in the capture list to avoid reference cycle.
Here's KVOObserver
...
class KVOObserver: NSObject {
private let callback: ()->Void
private let observee: NSObject
private let keyPath: String
private init(observee: NSObject, keyPath : String, callback: ()->Void) {
self.callback = callback
self.observee = observee
self.keyPath = keyPath;
}
deinit {
println("KVOObserver deinit")
observee.removeObserver(self, forKeyPath: keyPath)
}
override func observeValueForKeyPath(keyPath: String,
ofObject object: AnyObject,
change: [NSObject : AnyObject],
context: UnsafeMutablePointer<()>) {
println("KVOObserver: observeValueForKey: \(keyPath), \(object)")
self.callback()
}
class func observe(object: NSObject, keyPath : String, callback: ()->Void) -> KVOObserver {
let kvoObserver = KVOObserver(observee: object, keyPath: keyPath, callback: callback)
object.addObserver(kvoObserver, forKeyPath: keyPath, options: NSKeyValueObservingOptions.New | NSKeyValueObservingOptions.Initial, context: nil)
return kvoObserver
}
}