Reputation: 2222
Code:
class User
{
class var BoolProperty: Bool
{
get {
var anyObject: AnyObject? = getValue("BoolProperty")
if let value = anyObject as? Bool {
return value
}
else {
return false
}
}
set(value) {
setValue("BoolProperty", value: value)
}
}
private class func getValue(key: String) -> AnyObject?
{
var store = NSUserDefaults.standardUserDefaults();
return store.objectForKey(key) as AnyObject?
}
}
passes the test:
class UserTests: XCTestCase
{
func testFields()
{
User.BoolProperty = true
var result = User.BoolProperty
XCTAssertEqual(true, result)
}
}
but the following code doesn't pass the same test, which uses T instead of Bool for casting:
class User
{
class var BoolProperty: Bool
{
get {
return get("BoolProperty", defaultValue: false)
}
set(value) {
setValue("BoolProperty", value: value)
}
}
private class func getValue(key: String) -> AnyObject?
{
var store = NSUserDefaults.standardUserDefaults();
return store.objectForKey(key) as AnyObject?
}
private class func get<T>(key: String, defaultValue: T) -> T
{
var anyObject: AnyObject? = getValue(key)
if let value = anyObject as? T {
return value
}
else {
return defaultValue
}
}
}
it seems, that for some reason if let value = anyObject as? T
always returns false when casting to T.
In C# this is a classic example of working with untyped collections and I was wondering what's the right approach to achieve the same in Swift.
Upvotes: 1
Views: 1811
Reputation: 299275
The problem is that an NSNumber
is not a Bool
. It looks like a Bool
, and it can be converted to a Bool
, but it's really a different type. Your anyObject
is really an NSNumber
, and asking for it to be as? T
where T
is Bool
is too far. That's still likely a compiler bug, because it should be the same as a generic as without, but it's where the problem is happening.
You need to specialize the generic function to NSNumber
:
get {
return Bool(get("BoolProperty", defaultValue: NSNumber(bool: false)))
}
It's still probably worth opening a radar. It shouldn't behave differently with and without the generic.
That said, part of the trouble is the use of AnyObject
. A Bool
is not an AnyObject
. It's an Any
. When you try to assign it to an AnyObject
, it gets converted into an NSNumber
. I don't know of any documentation of this; I've worked it out empirically in playgrounds. Compare the results of let a:AnyObject = false
and let a:Any = false
.
In theory, this should fix it all:
var anyObject: Any? = getValue(key)
But it currently crashes the compiler.
Upvotes: 4