iosobjective-cswiftproperty-observer

Observe a property from an ObjC class in Swift


I am using a third party library for which I have an ObjC header file. In this header file there is a property that I would like to observe from my Swift code. My question is now: can I somehow extend the ObjC class without having the .m file such that I can observe a property whenever it is changed in Swift? I thought of using KVO but then I would need to change the implementation of the ObjC class?

Thanks for your help


Solution

  • Assuming your Objective-C class is key-value observing compliant, you can use addObserver(_:forKeyPath:options:context:). Here's an example:

    // Person.h
    #import <Foundation/Foundation.h>
    
    @interface Person : NSObject
    
    @property NSString * name;
    @property int age;
    
    - (id) initWithName:(NSString *) name
                    age:(int) age;
    
    @end
    
    // Person.m
    #import "Person.h"
    
    @implementation Person
    
    - (id) initWithName:(NSString *) name
                    age:(int) age
    {
        if (self = [super init]) {
            self.name = name;
            self.age = age;
        }
    
        return self;
    }
    
    @end
    

    And over in Swift:

    extension Person {
        override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
            if let keyPath = keyPath,
                let change = change,
                let oldValue = change[NSKeyValueChangeOldKey],
                let newValue = change[NSKeyValueChangeNewKey] {
    
                print("'\(keyPath)' has changed from \(oldValue) to \(newValue)")
            }
        }
    }
    
    let p = Person(name: "John", age: 42)
    
    // Start observing changes
    // In this case, the object will observe itself
    p.addObserver(p, forKeyPath: "name", options: [.New, .Old], context: nil)
    p.addObserver(p, forKeyPath: "age", options: [.New, .Old], context: nil)
    
    p.name = "Jack"
    p.age = 50
    
    // You must remove all observers before releasing the object
    p.removeObserver(p, forKeyPath: "name")
    p.removeObserver(p, forKeyPath: "age")