Why Does Implicitly Unwrapped Optional Not Unwrap in Dictionary of Type [String:Any]

Why does implicitly unwrapped optional not unwrap in dictionary of type [String : Any]

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:

  • Swift 3 incorrect string interpolation with implicitly unwrapped Optionals
  • Implicitly unwrapped optional assign in Xcode 8

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").

Why is this constant from a Dictionary not unwrapped?

The problem here is that it doesn't makes any sense to create a dictionary with a nil value. So instead of checking if the key exists before adding a value, I just add the value and check the value with a if let

In the end, this was the answer

    var referenceDatesIdentifiers: [String : Date] = [:]
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate)
}

Implicitly unwrapped property warning?

Since Swift 4, ImplicitlyUnwrappedOptional or ! as we knew it, became Optional.

Check:

let a: ImplicitlyUnwrappedOptional<Int> = 1

will spit out the error:

'ImplicitlyUnwrappedOptional' has been renamed to 'Optional'

So instead if we do:

let a: Int! = 1
print(type(of: a)) //will print "Optional<Int>"

It's still Optional<Int> but indicates to the compiler that it can be implicitly unwrapped.

Implicit Unwrapping is Part of a Declaration.


...

consider ! to be a synonym for ? with the addition that it adds a flag on the declaration letting the compiler know that the declared value can be implicitly unwrapped.

Ref: Reimplementation of Implicitly Unwrapped Optionals


Now getting to the main question:

If you do:

let a: Int! = 1

let b: Any = a
print(type(of: b)) //prints "Optional<Int>"

It will give the following warning:

Expression implicitly coerced from 'Int?' to 'Any'

or as per Xcode 11

Coercion of implicitly unwrappable value of type 'Int?' to 'Any' does not unwrap optional

Note here that we tried to get a non-optional Any out of an Int? which means we were basically expecting an Int but just by specifying Any it won't also unwrap the Optional.

It will remain an Optional, and this is the meaning behind that warning.


Solutions:

To handle this warning gracefully, we can do any of the following:

let a: Int! = 1

let b: Any? = a
type(of: b) //Optional<Any>.Type

let c: Any! = a
type(of: c) //Optional<Any>.Type

let d: Any = a!
type(of: d) //Int.Type

EDIT: (based on comment)

! instead of ? have any practical difference for the programmer?

! tells the compiler that it can be implicitly unwrapped so it can help ease in the need for optional chaining.

Example:

  • With ?

    class A {
    var n: Int? = 1
    }

    class B {
    var a: A? = A()
    }

    let b: B? = B()
    print(b?.a?.n)

    /*
    but the following won't compile as compiler
    will not implicitly unwrap optionals

    print(b.a.n)
    */
  • With !

    class A {
    var n: Int! = 1
    }

    class B {
    var a: A! = A()
    }

    let b: B! = B()
    print(b.a.n) //compiler will implicitly unwrap `!` similar to print(b!.a!.n)

    //also... you can still safely unwrap if you want
    print(b?.a?.n)
    • b.a.n and b?.a?.n will both give an Optional<Int> at the end
    • Ofcourse if b or a is nil then b.a.n will crash because it's implicitly unwrapping b and a to get to n which is Optional<Int>
    • To get Int instead of Optional<Int>, you would do b.a.n! and so in your case you would do: print(residenceId!) to get Int

I hope I made sense

Implicit Unwrapped Optional

As others have said, IUOs (implicitly unwrapped optionals) are now regular optionals with some hints to the compiler that gives it permission to unwrap them.

That being said, print is handled differently. Try using an assignment to a non-optional variable instead. That's a better test, e.g:

var a : Int! = nil
let b: Int = a

Implicitly unwrapped optional assign in Xcode 8

This is a consequence of SE-0054 Abolish ImplicitlyUnwrappedOptional type which has been implemented in Swift 3. Extract from that proposal (emphasis added):

However, the appearance of ! at the end of a property or variable declaration's type no longer indicates that the declaration has IUO type; rather, it indicates that (1) the declaration has optional type, and (2) the declaration has an attribute indicating that its value may be implicitly forced. ...

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?. For example, in the following code:

let x: Int! = 5
let y = x
let z = x + 0

… x is declared as an IUO, but because the initializer for y type checks correctly as an optional, y will be bound as type Int?. However, the initializer for z does not type check with x declared as an optional (there's no overload of + that takes an optional), so the compiler forces the optional and type checks the initializer as Int.

In your case, the assignment

let foo = implicitlyUnwrappedOptionalString

makes foo a strong optional, as in the example let y = x
from the proposal.

You could make foo an IUO by adding an explicit type annotation

let foo: String! = implicitlyUnwrappedOptionalString

but generally you should try to get rid from IUOs in your code,
as stated in the same proposal:

Except for a few specific scenarios, optionals are always the safer bet, and we’d like to encourage people to use them instead of IUOs.

Why don't you need to unwrap an optional type value when being set?

An optional is a box. The box can either contain nothing (which is called nil) or it can contain something of a specific type (a String in your example). You unwrap an optional to access the value inside in the box.

When you are assigning a value to an optional, you just need to assign the value to the box itself. There is no need to unwrap anything, because you are just putting a value into the box. Swift either empties the box, if you assign nil, or it wraps the value by putting it into the box.

Unwrapping is for accessing a value that is already in the box.


Answering your question from a comment on another answer...

But why Optional binding don't need unwrapping? i think i if let constantName = some Optional is kind of assignment too

Optional binding is an unwrapping operation and an assignment operation. It says "if there is a value inside the box, assign it to this new variable and enter the then clause, otherwise proceed to the else clause if it exists".

var optionalValue: String? = "hello"

if let value = optionalValue {
// value is the contents of the box, it has type String
print("value is \(value)")
} else {
// the optional binding failed because the box is empty
print("optionalValue is nil")
}

Swift 3: Cannot automatically unwrap optional before setting it to Label

In Swift 3, an implicitly-unwrapped optional is the same as an optional, except that it will implicitly unwrap in contexts that require it. For instance, if you had a func foo(i: Int), you could write foo(i: leftNumber) and the compiler would perform the unwrap operation for you.

String interpolation is not a context where unwrapping is required. As you can use an optional there, Swift prefers the optional case and does not unwrap for you.

Tutorials that you find online may either not have been updated for Swift 3, or there may have been a misunderstanding.

Implicitly unwrapped optional Closure in a method argument

The implicitly unwrapped optional means you technically can pass nil, but whether or not it crashes depends on the rest of the code. In this case, it appears that the case where completionHandler is nil is handled properly (so you don't have to provide one). I would simply say this is a sub-optimal API :)



Related Topics



Leave a reply



Submit