I am attempting to dynamically clone a class object in Swift using reflection and the setValue function. This class contains an enum property that is backed with an Int, which is causing this dynamic reflection to crash:
@objc enum Status : Int {
case green
case yellow
case red
}
@objc class State : NSObject {
@objc var status : Status
init(_ status: Status) {
self.status = status
}
}
func testReflectiveClone() {
let state1 = State(.green)
let state2 = State(.yellow)
let state1Mirror = Mirror(reflecting: state1)
for property in state1Mirror.children.enumerated() {
let label = property.element.label
state2.setValue(property.element.value, forKey: label!) //crashes here
}
}
This test function is throwing the following error in XCode:
-[__SwiftValue longLongValue]: unrecognized selector sent to instance 0x600001e5a340 (NSInvalidArgumentException)
Is it even possible to dynamically set enum values? What modification would I need to make to get this to work?
The problem here is that you’re mixing Swift reflection with Objective-C key-value-coding. property.element.value returns a Swift Any that boxes up the native Status enum value. This is where the __SwiftValue type comes from. You then pass that to a KVC routine, setValue(_:forKey:), which is expecting the passed-in value to follow Objective-C norms, that is, to support the -longLongValue method.
You could make this work by going all in on KVC:
let label = property.element.label!
let v = state1.value(forKey: label)
state2.setValue(v, forKey: label)
But, sheesh, you’re playing with fire here. If you continue down this path you will run into other problems.
Is this something you plan to ship to a wide array of users? Or something that you’re only going to use yourself?
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"