Updated Answer - For macOS
With Xcode 8 beta 6, Swift no longer implicitly bridges Swift value types to Foundation class types. Which means if a function is expecting an NSNumber
and you pass it an Int
variable, you will have to explicitly cast it to NSNumber
. This is not necessary for an integer literal because Swift will still infer the type correctly.
Why does 1 compile, but 2 does not?
1 compiles because Swift is able to infer the type of 20
to be NSNumber
, so ["a": 20]
works as a [String: NSNumber]
.
2 doesn't compile, because the type of a
is already established as Int
, so you need to explicitly convert that to NSNumber
. Xcode's fix-it suggests NSNumber(a)
, but sadly that doesn't compile. Use NSNumber(value: a)
or a as NSNumber
.
Why do 2 and 3 have different error messages?
For 2, you are providing a dictionary literal ["a": a]
so Swift examines the types of each key and value to see if it matches the types the dictionary it expects. Since a
is an Int
and the value is a NSNumber
, you get the error Cannot convert value of type 'Int' to expected dictionary value type 'NSNumber'. It wants you to provide the conversion.
For 3, you are providing a variable of type [String, Int]
. Swift tells you that it can't convert that to [String, NSNumber]
. It can, but not without an explicit cast due to the change in Xcode 8 beta 6.
Why does 4 compile, but 5 does not?
4 compiles because you are now providing the explicit cast to [String: NSNumber]
that 3 lacked.
5 does not compile because again you are providing a dictionary literal and Swift examines each of the keys and values to make sure they are the right types. It will not convert the Int
to an NSNumber
without an explicit cast, so the error here is Cannot convert value of type 'Int' to expected dictionary value type 'NSNumber'. The point is that Swift will not cast the individual keys and values of a dictionary literal when you cast it to a dictionary type. You have to provide that cast directly for each one.
Previous Answer - For iOS
With Xcode 8 beta 6, the type of the argument metrics
has changed to [String: Any]?
. Now, the first 4 examples compile, and the 5th does not. Your first two questions are no longer valid. The only question left is:
Why does 4 compile, but 5 does not?
Statement 4 (met as [String: NSNumber]
) compiles because met
has type [String: Int]
and Swift can cast [String: Int]
to [String: NSNumber]
. In this case, it is looking at the dictionary as a whole. Swift knows how to convert an Int
to an NSNumber
, but it won't do so without you asking it to do so explicitly. In this case, since you are presenting a dictionary of type [String: Int]
and asking it to convert that to [String: NSNumber]
, you are asking it to convert the Int
to an NSNumber
.
In statement 5, you are casting a dictionary literal ["a": a]
to a dictionary type as [String: NSNumber]
. The error message is:
Cannot convert value of type 'Int' to expected dictionary value type 'NSNumber'
In this case, Swift is looking at the individual types, checking to see that "a"
is a String
and a
is a NSNumber
. Casting a dictionary literal to a type does not explicitly cast each key and value to the corresponding type. In that case, you are merely presenting them and saying that they are already that type. Due to a new change in Xcode 8 beta 6, Swift will no longer implicitly convert Swift value types to bridged Foundation types. So Swift wants you to explicitly convert the Int
a
to an NSNumber
.
There are two ways to make Swift happy:
["a": NSNumber(value: a)] as [String: NSNumber]
["a": a as NSNumber] as [String: NSNumber]
Of course, now in both cases the dictionary literal can be inferred to be [String: NSNumber]
so the cast in unnecessary.
Also, since metrics
is now [String: Any]
, it makes no sense to convert ["a": a]
to [String: NSNumber]
when [String: Int]
would do.