7

I have a third party library class whose source is not open to me. For example like this:

class MyClass: NSObject {
    let myProperty = 10
} 

I can get myProperty value like this.

let test = MyClass().value(forKey: "myProperty")  // 10

I want to check whether myProperty exists in MyClass. Reason is I don't want my app to crash if the 3rd party class implementation changes in future.

For testing I've tried

guard let test = MyClass().value(forKey: "myProperty1") else { return }  // crash

if let test = MyClass().value(forKey: "myProperty1") { }  // crash

do {
    let test = try MyClass().value(forKey: "myProperty1")  // crash
} catch { }

In every way I get crash.

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__lldb_expr_80.MyClass 0x608000223a00> valueForUndefinedKey:]: this class is not key value coding-compliant for the key myProperty1.'

Warif Akhand Rishi
  • 23,920
  • 8
  • 80
  • 107
  • Dont think there's anyway to prevent this, you must update your app or ask the lib's dev to change to more convenience way of accessing property – Tj3n Dec 26 '16 at 07:51
  • 3
    In Swift you cannot catch exceptions at runtime. – vadian Dec 26 '16 at 08:00

2 Answers2

5

When valueForKey: is invoked for an undefined key, the runtime forwards the call to valueForUndefinedKey:. The default implementation of the later simply raises NSUndefinedKeyException.

We can't catch the exception from Swift code, but we can override the method valueForUndefinedKey: in an extension and return some error:

extension NSObject {
    @objc
    func value(forUndefinedKey key: String) -> Any {
        NSError(domain: "Dynamic", code: 404, userInfo: [NSLocalizedDescriptionKey: "Accessing undefined key: '\(key)'"])
    }
}
Hejazi
  • 16,587
  • 9
  • 52
  • 67
1

You must find a better solution by talking to lib dev

You can use Mirror reflection and check its objects label

let myClass = Mirror(reflecting: MyClass())
for (_, attr) in myClass.children.enumerated() {
  if let propertyName = attr.label, propertyName == "myProperty"{
   print(propertyName, attr.value)
  }
}
Hamza Ansari
  • 3,009
  • 1
  • 23
  • 25