Using setValue(value, forKey: key) on Int? types fires non key value coding-compliant method
Asked Answered
Z

2

26

I'm successfully using the setValue(value, forKey: key) method in my NSKeyValueCoding compliant Swift NSObject subclass.

This works perfectly well on String optionals, e.g.

var name:String?

However, on Int optionals, it fails, triggering the undefined key method that I have overridden for debugging purposes:

override func setValue(value: AnyObject!, forUndefinedKey key: String!) {
    println("\(self) this class is not key value coding-compliant for the key \(key)")
}

So for example, a key of myId with a good integer value would trigger the undefined key method above.

var myId:Int?

If I change the above definition to be non-optional, then everything works fine:

var myId:Int = 0

With myId as an optional, I have tried absolutely everything I can think of in the way of casting, unwrapping, initialising, and so on. It just doesn't see the class as key value compliant for those numeric values.

I know that it is a good numeric value. Changing the var declaration to String? crashes. It also looks fine in lldb:

Printing description of key:
myId
key NSObject    0x00007fb8d530ca20  0x00007fb8d530ca20
k   NSString    "myId"  0x00007fa2aa942f20
value   __NSCFNumber *  Int64(4348129)  0xb000000004258e13
Printing description of value:
4348129
(lldb) 

So, the question is, has anyone used - in Swift - the NSKeyValueCoding method setValue(value, forKey: key) on an Int type successfully?

Zootoxin answered 30/9, 2014 at 10:44 Comment(1)
Could this be because Int is of type Any and not AnyObject. I tried parsing an object to a dictionary and back and had similar problems. See github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/… You can see I tried parsing the Any to an AnyObject. Nullable values are still a problem, but it works on an Int.Tranship
L
24

KVO cannot function with pure Swift optionals because pure Swift optionals are not Objective-C objects. Swift forbids the use of dynamic or @objc with generic classes and structures because there is no valid Objective-C equivalent, and so the runtime is not setup to support KVO on instances of those kinds of objects. As for why it works with String?, that type is toll-free bridged to NSString, so it is semantically equivalent to NSString *, an Objective-C type that the runtime knows full-well how to deal with. But compare that to Int? whose semantic equivalent would be Optional<Int>, not UnsafePointer<Int> or NSNumber * like you might expect. For now, you'll need to convince the typechecker that it is safe to represent in Objective-C by using NSNumber!.

This is completely backwards and, in my opinion, an unfortunate limitation of the type system. For any engineers that come across this post, see rdar://18624182.

Leoraleos answered 11/10, 2014 at 22:15 Comment(2)
I think there are a few technical inaccuracies in this post. 1) I've changed Int? to NSNumber?, and now it's fully KVO compliant. 2) KVO can function with optionals, as I can use UIColor?, NSNumber?, and other objc types. 3) Optionals are specially handled generics, and even added to Objc using nonnullable, nullable, .. etc.Blende
1 is implied by the final sentence of the first paragraph, 2 by the 3rd sentence, and 3 is wrong. Optionals are bridged to a limited set of ObjC types by the runtime, but they are not completely representable with just the new nullable attributes. Besides, this is about Swift structures and KVO, not Objective-C structures being bridged in.Leoraleos
S
3

If you're willing to ditch Swift types change:

var myId:Int?

to:

var myId:NSNumber?
Suspension answered 8/4, 2015 at 22:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.