Under the rules set out by SE-0054, IUOs are only force unwrapped in contexts that demand their unwrapped type. In your case, the IUO doesn't need to be force unwrapped in order to be coerced to Any
(as Any
can represent any value), so it isn't.
This behaviour is discussed in more detail in these Q&As:
The fact that you end up with an ImplicitlyUnwrappedOptional
value in your dictionary is legacy behaviour that has been removed in the latest Swift snapshots, in the future you will end up with an Optional
value instead (as IUO is no longer a type).
One important thing to note here however (that I'm sure will trip up people) is that the printing of IUOs got changed in 4.1.
In Swift 4.0.3, your example prints like this:
var aString: String! = "hello"
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": hello]
giving you the illusion that the IUO was force unwrapped when coerced to Any
. This however is just how IUOs were printed in Swift 4.0.3 – if they had a value, then they would print as that value, otherwise they would print as nil
:
var aString: String! = nil
var params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": nil]
The reason why this changed in Swift 4.1 is that ImplicitlyUnwrappedOptional
's conformance to Custom(Debug)StringConvertible
was removed in this commit in order to make progress towards removing the type itself. So now ImplicitlyUnwrappedOptional
values get printed using Swift's default printing mechanism (using reflection).
So, in a dictionary, you get the IUO's default debugDescription
, which looks like this:
let aString: String! = "hello"
let params : [String : Any] = [
"myString" : aString
]
print(params)
// This prints ["myString": Swift.ImplicitlyUnwrappedOptional<Swift.String>.some("hello")]
If you had printed it on its own, you would get its default description
, which looks like this:
let aString: String! = "hello"
print(aString) // some("hello")
This is because in Swift 4.1, the ImplicitlyUnwrappedOptional
type is implemented in the same way as Optional
, an enumeration with two cases:
public enum ImplicitlyUnwrappedOptional<Wrapped> : ExpressibleByNilLiteral {
// The compiler has special knowledge of the existence of
// `ImplicitlyUnwrappedOptional<Wrapped>`, but always interacts with it using
// the library intrinsics below.
/// The absence of a value. Typically written using the nil literal, `nil`.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
// ...
}
For an IUO with a payload value, Swift's default reflection will therefore print it as the case some
containing the wrapped value.
But this is only temporary; the IUO type is currently (in Swift 4.1) deprecated, however it will be removed in Swift 4.2. The compiler was internally using the IUO type in quite a few places, which took quite a bit of work to remove. Therefore in 4.2 you'll have actual Optional
values in your dictionary, which will print like Optional("hello")
.