Swift KVO - Observable Renaming Properties

I am assembling a class that has several states, as defined by an enumeration, and a read-only state property that returns the current state of the instance. I was hoping to use KVO methods to monitor state changes, but this is not possible:

dynamic var state:ItemState // Generates compile-time error: Property cannot be marked dynamic because its type cannot be represented in Objective-C

I suppose I could represent each state as Int or String, etc., but is there a simple alternative workaround that would preserve the type safety that the enumeration would otherwise provide?

Vince.

+4
source share
2 answers

. "raw", .

KVO "" , .

, , , , .

.

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
        }
    }
}

:

, Swift, , . :

  • NSObject.
  • , observeValueForKeyPath: BlahBlah...
  • , , .

KVOObserver, :

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)")
        }
    }
}

[unowned self] , .

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
    }
}
+13

, swift 2+, , rawValue. .

  • , NSObject ( )
  • @objc
  • Int
  • dynamic.
class SomeModel : NSObject {                          // (1) extend from NSObject
    @objc                                             // (2) mark enum with @objc
    enum ItemState : Int, CustomStringConvertible {   // (3) extend enum from Int
        case Ready, Set, Go

        // implementing CustomStringConvertible for example output
        var description : String {
            switch self {
            case .Ready: return "Ready"
            case .Set: return "Set"
            case .Go: return "Go"
            }
        }
    }

    dynamic var state = ItemState.Ready               // (4) declare property as dynamic
}

:

class EnumObserverExample : NSObject {
    private let _model : SomeModel

    init(model:SomeModel) {
        _model = model
        super.init()
        _model.addObserver(self, forKeyPath:"state", options: NSKeyValueObservingOptions.Initial, context: nil)
    }
    deinit {
        _model.removeObserver(self, forKeyPath:"state", context: nil)
    }

    override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if "state" == keyPath {
            print("Observed state change to \(_model.state)")
        }
    }
}

let model = SomeModel()
let observer = EnumObserverExample(model:model)
model.state = .Set
model.state = .Go

:

Observed state change to Ready    (because .Initial was specified)
Observed state change to Set
Observed state change to Go
+11

All Articles