5

I would like to understand why I don't get ImplicitlyUnwrappedOptional when I do params["bar"] = str but I get it when I declare params with the same force unwrapped variable.

See the playground below:

import UIKit

var str: String!

str = "Hello"

var params: [String: Any] = [
    "foo": str
]

params["bar"] = str

print(params)

// ["bar": "Hello", "foo": Swift.ImplicitlyUnwrappedOptional<Swift.String>.some("Hello")]
allaire
  • 5,995
  • 3
  • 41
  • 56
  • 1
    Because `ImplicitlyUnwrappedOptional` counts as `Any`. Why would it be coerced (and forcefully unwrapped) to `String`? `String` is not more `Any` than IUO is. – Alexander Apr 26 '18 at 21:25
  • @Alexander so why is it different for “bar” then? – algrid Apr 26 '18 at 21:32
  • Good point. Interesting! – Alexander Apr 26 '18 at 21:46
  • What version of Swift is this? IUO doesn't exist in 4, and I can't reproduce this in 3.0.1 on linux – Alexander Apr 26 '18 at 21:47
  • 5
    Compare https://stackoverflow.com/q/49609528/2976878. In the case where you're doing `params["bar"] = str`, `Dictionary`'s `subscript` takes an `Optional` value, which (in Swift 4.1) an IUO can be implicitly converted to, or (in Swift 4.2) an IUO is. The value is removed if the optional is `nil`, otherwise the unwrapped value is inserted into the dictionary. – Hamish Apr 26 '18 at 21:48
  • You can get the same result for `bar` by explicitly casting `str as Any` when making the assignment: `params["bar"] = str as Any`. This makes `"bar": Swift.ImplicitlyUnwrappedOptional.some("Hello")` instead of `"bar": "Hello"` because in that case the optional is not unwrapped before assigning the value to the dictionary. – vacawama Apr 27 '18 at 01:16
  • 1
    Hi guys thanks for your comments, I'm using Xcode 9.4 with Swift 4.1. I just noticed that there was a regression in my app because of this, using Alamofire and URLEncoding. @Hamish is this the same explanation as the one you linked? Feel free to answer and I would glady accept this, thanks! – allaire Apr 27 '18 at 12:15
  • @allaire Sorry for taking so long to get back to this; I've posted an answer :) – Hamish May 12 '18 at 14:22
  • Look at Swift 4.2 : Abolish ImplicitlyUnwrappedOptional type Proposal: SE-0054 – claude31 May 12 '18 at 14:44
  • @Hamish Please give this answer i got same problem. https://stackoverflow.com/questions/50617135/core-data-appears-to-lose-data-after-xcode-upgrade – AbecedarioPoint May 31 '18 at 10:07

1 Answers1

1

In Swift 4.1, when you do:

var str: String!

str = "Hello"

var params: [String: Any] = [
    "foo": str
]

The ImplicitlyUnwrappedOptional (IUO) value is coerced to Any, which is why it appears as an IUO inside your dictionary. It won't be force unwrapped, because the compiler will only force unwrap an IUO when the context demands its unwrapped type, which isn't the case with Any.

However the fact that you end up with an ImplicitlyUnwrappedOptional value is legacy behaviour. With the removal of the IUO type in Swift 4.2, you'll get an Optional value inside your dictionary instead, which will print as Optional("Hello").

There's more discussion of the above behaviour in this Q&A:

When you do:

params["bar"] = str

You're using Dictionary's subscript(key: Key) -> Value?, which takes an Optional value – performing a removal if nil is passed, otherwise doing an insertion of the unwrapped value.

  • In Swift 4.1, the IUO value str will be implicitly converted to an Optional which can then be passed to the subscript.
  • In Swift 4.2, the IUO type has been removed, so str already is an Optional, which can be passed straight to the subscript without any intermediate conversions.

In both cases, str's unwrapped value is inserted into the dictionary, which is why you see it as being unwrapped. If str had been nil, no value for the key "bar" would have been inserted.

Hamish
  • 78,605
  • 19
  • 187
  • 280