3

I have a very specific issue where, if I have a swift class which inherits from an objective-c class, and it has a dynamic property.

Now, on the +initialize, I am injecting getter and setters into the swift(:NSObject) class, and these work no problem, except for a slight issue when setting values from an init overload.

So, my swift class looks like this:

class TestClass: BaseClass {

    dynamic var testStringProperty: String?

    init(initialValue: String) {
        super.init()
        self.testStringProperty = initialValue;
        // does not trigger the get/set methods
        // a po self.testStringProperty will output 'nil'
        let xyz = self.testStringProperty;
        // xyz is actually set to the value of initialValue, but it does not trigger the getter.
    }

}

And the objective-c class that bases the swift is as follows:

static id storedVar;

@implementation BaseClass

+ (void)initialize {
    // we are going to exchange getter/setter methods for a property on a SwiftClass, crude but demonstrates the point
    if(![[self description] isEqualToString:@"BaseClass"]) {

        IMP reader = (IMP)getPropertyIMP;
        IMP writer = (IMP)setPropertyIMP;
        const char* type = "@";
        NSString* propertyName = @"testStringProperty";

        IMP oldMethod = class_replaceMethod([self class], NSSelectorFromString(propertyName), reader, [[NSString stringWithFormat:@"%s@:", type] UTF8String]);
        NSString* setMethod = [NSString stringWithFormat:@"set%@%@:", [[propertyName substringToIndex:1] uppercaseString], [propertyName substringFromIndex:1]];
        oldMethod = class_replaceMethod([self class], NSSelectorFromString(setMethod), writer, [[NSString stringWithFormat:@"v@:%s",type] UTF8String]);

    }
}

static id getPropertyIMP(id self, SEL _cmd) {

    return storedVar;

}

static void setPropertyIMP(id self, SEL _cmd, id aValue) {

    storedVar = aValue;

}

@end

Long story short, whilst in the call to init(initialValue: String), the getters and setters are not triggered, but immediately after the call to init has completed they work.

This is despite the call to initialize completing successfully and the methods being replaced.

But outside of the init function, the get/set behave as expected.

Here is where it get's called.

let test = TestClass(initialValue: "hello");
test.testStringProperty = "hello"

A po test.testStringProperty after the creation of the object, will output nil. But the subsequent assignment, triggers all the correct methods.

It only fails when assigning within the init. Everywhere else it works like a charm.

I would like to get this to work within the initializer if possible, i'm not sure if there is another way to work around it.

Here is a link to the sample app that replicates the issue:

https://www.dropbox.com/s/5jymj581yps799d/swiftTest.zip?dl=0

Adrian_H
  • 1,548
  • 1
  • 14
  • 27

0 Answers0