In the former version, to get a float value from a [String: Any]
dictionary, I can use let float = dict["somekey"] as? Float
, but in swift4.1, it doesn't work. It seems the type of dict["somekey"]
has been implicitly inferred as Double
before I get it, so casting from Double
to Float
always fails. I wonder if it is a new characteristic or just a bug.
--Here is the update.
I re-downloadeded an Xcode9.2 and did some experiments, now I think I figure out what's going on. Here is the test code:
let dict: [String : Any] = ["key": 0.1]
if let float: Float = dict["key"] as? Float {
print(float)
} else {
print("nil")
}
let dict1: [String : Any] = ["key": NSNumber(value: 0.2)]
if let float: Float = dict1["key"] as? Float {
print(float)
} else {
print("nil")
}
let number = NSNumber(value: 0.3)
if let float: Float = number as? Float {
print(float)
} else {
print("nil")
}
let number1 = NSNumber(floatLiteral: 0.4)
if let float = number1 as? Float {
print(float)
} else {
print("nil")
}
Running this code in Playground of Swift4 and Swift4.1, the results are different. In Swift4, the results are nil
0.2
0.3
0.4
, and In Swift4.1 the results are nil
nil
nil
nil
. From the result, I can learn two points:
1. When we convert JSON data into a [String : Any] dictionary
with the JSONSerialization
class, the numeric value is saved as an NSNumber
object, but not Int
, Double
or Float
.
2. In Swift4, we can use let float = NSNumberOjbect as? Float
to get a Float
value, but in Swift4.1 we can't. But still, we can get Int
or Double
value in this way, either in Swift4 or Swift4.1.
Finally again, is this a new feature or a bug? If someone knows, can you guys show up the announcement link?
You need to distinguish two cases (in Swift 3, it was three cases):
Any
containing a Swift nativeDouble
Any
containing anNSNumber
(In Swift 3, there was type preserving
NSNumber
case other than the normalNSNumber
.)When you create a native Swift
Dictionary
such as[String: Any]
, and setDouble
value in a normal way like this in your update:In this case,
Any
holds the metadata representingDouble
and the raw value0.1
asDouble
.And casting
Any
holdingDouble
toFloat
always fails. AsDouble
toFloat
cannot be converted withas
-castings.But, in case of
Any
holdingNSNumber
, as always happens when theArray
orDictionary
is bridged fromNSArray
orNSDictionary
, the behaviors are different between former Swifts and Swift 4.1.The result of
JSONSerialization
matches this case.In former Swifts, (normal)
NSNumber
toFloat
was an always-success operation.And in Swift 4.1, the behavior has changed with the new feature which I have shown in my comment:
SE-0170 NSNumber bridging and Numeric types
I omit the third case once found in Swift 3, it's past.
But it is very important how you get your
Dictionary
orArray
, and how you set the value to them, to solve the issueAny
toFloat
.Finally again, is this a new feature or a bug? If someone knows, can you guys show up the announcement link?
This is a new feature, not a bug. See the link above, and related threads below.
Unable to bridge NSNumber to Float Swift 3.3
Unexpected behavior when casting an NSNumber to Float