I need to check if an object's class has a member which could be used for assigning a value. Let's say there's a class:
class MyClass: NSObject {
var myVar : AnyObject!
// ...
}
I tried this objC style but properties are a different beast in Swift and second line always returns false:
let myClass: MyClass = MyClass()
let hasClassMember = myClass.respondsToSelector(Selector("setMyVar:"))
if hasClassMember {
myClass.performSelector("setMyVar:", withObject: "Context" as! AnyObject)
}
I assume Swift doesn't have a powerful reflection as ObjC has, but is there any solution?
UPDATE It turns out my code works, however I have simplified it for demo purposes. The weird part is respondsToSelector stops working as soon as I change the type of myVar to be an instance of some custom class, let say ClassB. Feels it a bit magic... :)
UPDATE2 Finally I found the problem. I haven't wrote the full code, thought the problem is elsewhere, anyway, here's the full code. MyClass variable is actually a type of some other AnotherClass which implements AnotherClassProtocol
protocol AnotherClassProtocol {
//…
}
class AnotherClass: AnotherClassProtocol {
//…
}
class MyClass: NSObject {
var myVar : AnotherClass!
// ...
}
This was causing myClass.respondsToSelector(Selector("setMyVar:")) always return false. It turns out the problem was because I omitted extending of NSObject in my AnotherClass declaration. It starts working as expected after I fixed that:
class AnotherClass: NSObject, AnotherClassProtocol {
//…
}
I'm still learning Swift and thought NSObject is not needed as the compiler was not complaining and everything extend from NSObject anyway (at least in ObjC). Well, I'm glad I found this nasty bug.
A slightly nicer way than checking for selectors would be to use a protocol.
protocol MyClassProtocol {
var myVar : AnyObject! {get set}
}
class MyClass: NSObject, MyClassProtocol {
var myVar : AnyObject!
// ...
}
...
let c = MyClass()
if let conformingMyClass = c as? MyClassProtocol { // returns true
print("yay")
conformingMyClass.myVar = "Context"
} else {
print("nay")
}
This way it's a lot more explicit which classes do and don't have your property and you don't have to muck about with selectors (much more Swift-y).
import Foundation
class MyClass: NSObject {
var myVar1 : AnyObject!
// ...
}
let myClass: MyClass = MyClass()
let hasClassMemberMyVar1 = myClass.respondsToSelector(Selector("setMyVar1:")) // true
let hasClassMemberMyVar2 = myClass.respondsToSelector(Selector("setMyVar2:")) // false
it works for me ...
UPDATE, based on OP notes
import Foundation
class C:NSObject {}
class MyClass: NSObject {
var myVar1 : C? // Objective-C representable
var i: Int = 0 // Objective-C representable
var j: Int? = 10
}
let myClass: MyClass = MyClass()
let hasClassMemberMyVar1 = myClass.respondsToSelector(Selector("setMyVar1:")) // true
let hasClassMemberMyVar2 = myClass.respondsToSelector(Selector("setMyVar2:")) // false
let hasClassMemberI = myClass.respondsToSelector(Selector("setI:")) // true
let hasClassMemberJ = myClass.respondsToSelector(Selector("setJ:")) // false, because Optional<Int> is not representable in Objective-C !!!
print(myClass.i.dynamicType, myClass.j.dynamicType) // Int Optional<Int>
with class type properties only
import Foundation
class C:NSObject {}
class C1 {}
class MyClass: NSObject {
var c : C?
var cO1: C = C()
var cO2: C!
var c1: C1 = C1()
var c2: C1?
var c3: C1!
}
let myClass: MyClass = MyClass()
let hasClassMemberC = myClass.respondsToSelector(Selector("setC:")) // true
let hasClassMemberCO1 = myClass.respondsToSelector(Selector("setCO1:")) // true
let hasClassMemberCO2 = myClass.respondsToSelector(Selector("setCO2:")) // true
let hasClassMemberC1 = myClass.respondsToSelector(Selector("setC1:")) // false, class C1 is not Objective-C representable ...
let hasClassMemberC2 = myClass.respondsToSelector(Selector("setC2:")) // false, Optional<C1> is not Objective-C representable ...
let hasClassMemberC3 = myClass.respondsToSelector(Selector("setC3:")) // false, ImplicitlyUnwrappedOptional<C1> is not Objective-C representable ...
Swift 3 version of Ryan Huubert's answer:
class MyClass: NSObject {
var myVar : AnyObject!
// ...
}
let myClass = MyClass()
myClass.responds(to: Selector("myVar")) // returns true
myClass.responds(to: Selector("myVar:")) // returns false
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With