Keiwan Keiwan - 2 months ago 38
Swift Question

Swift 3 incorrect string interpolation with implicitly unwrapped strings

Why are implicitly unwrapped strings not unwrapped when using string interpolation in Swift 3?

Example:
Running the following code in the playground

var str: String!
str = "Hello"

print("The following should not be printed as an optional: \(str)")


produces this output:

The following should not be printed as an optional: Optional("Hello")


Of course I can concatenate strings with the
+
operator but I'm using string interpolation pretty much everywhere in my app which now doesn't work anymore due to this (bug?).

Is this even a bug or did they intentionally change this for Swift 3?

Answer

As per SE-0054, ImplicitlyUnwrappedOptional is no longer a distinct type. Instead, it's now the same type as a regular Optional – it just has an attribute that allows the compiler to force unwrap it in situations where it cannot be type checked as one.

As the proposal says (emphasis mine):

If the expression can be explicitly type checked with a strong optional type, it will be. However, the type checker will fall back to forcing the optional if necessary. The effect of this behavior is that the result of any expression that refers to a value declared as T! will either have type T or type T?.

With string interpolation, an implicitly unwrapped optional (note this doesn't just apply to String!) can be treated as a strong optional (as you can use string interpolation with any type) – therefore the compiler will favour treating it as such, and not force unwrap it.

If you wish for it to be force unwrapped, then you can use the force unwrap operator !:

var str: String!
str = "Hello"

print("The following should not be printed as an optional: \(str!)")

or you can cast to String in order to force the compiler to implicitly force unwrap it for you:

print("The following should not be printed as an optional: \(str as String)")

both of which, of course, will crash if str is nil.