4

"Value of optional type 'String??' not unwrapped; did you mean to use '!' or '?'?" -

I got this weird compiler error today, which was entirely confusing due to the two question marks after String.

I have a dictionary s, of type [String : String?], and a function which accepts all arguments as String?s. Specifically (from 5813's method of copying user-selected information into a dictionary), I have an elaborated version of the following:

func combine(firstname: String?, lastname: String?) {...}

var text = combine(s["kABPersonFirstNameProperty"], lastname: s["kABPersonLastNameProperty"])

I'm getting the error on the second line, and I'm wondering why it's so. If the values in s are of type String?, shouldn't that be accepted by combine(), since it's arguments are supposed to be of the same type? Why, then, would I get this error and how can I fix it?

Community
  • 1
  • 1
Randoms
  • 2,110
  • 2
  • 20
  • 31

3 Answers3

3

Dictionary<T1, T2>[key] returns T2?. This is to return nil in case key doesn't exist.

So if T2 is String?, s[key] returns String?? You cannot pass String?? as String?

You can call like this to unwrap and prepare for non-existing key as well

var text = combine(s["kABPersonFirstNameProperty"] ?? nil, lastname: s["kABPersonLastNameProperty"] ?? nil)

By the way, code below will not set value to nil but remove the entire entry from the dictionary

s[key] = nil

If you want the value to be nil instead of removing entry, you will have to do this

s[key] = nil as String?
hebinda
  • 519
  • 6
  • 14
2

It is once optional because your dictionary value is optional. And it is optional again, because dictionary[key] returns optional. So you need to unwrap it twice.

Try this in a playground to understand the problem (and see possible solution):

let oos: String?? = "Hello"
print(oos)

if let os = oos { // Make String?
    print(os)
    if let s = os { // Make ordinary String
        print(s)
    }
}

Prints:

 Optional(Optional("Hello"))

 Optional("Hello")

 Hello

But you could use other ways than if let to unwrap, too. For example:

 print(oos! ?? "n/a")

Will force unwrap it once and then print either the inner String or n/a in case of nil...

MirekE
  • 11,515
  • 5
  • 35
  • 28
  • HI MirekE, thanks for the quick response. The values of the dictionary will, at times, be `nil`, so what marks should I use, and where? I can't use the `!` because it might not have a value, and I tried `s[key]??`, but got another error. – Randoms Aug 07 '15 at 03:29
  • There are many options, for example you can use `if let` twice. – MirekE Aug 07 '15 at 03:41
0

Maybe the easiest solution would be to make the dictionary hold String instead of String?. Then you don't have the unwrapping problems which are described in other solutions.

Or do you really have to store String? types? Do you want to differentiate between 'key exists but holds nil' and 'key does not exist'?

Tali
  • 861
  • 8
  • 17
  • Sometimes it's necessary to supply different logic meanings to both "value not exist" and "nil value exists" – hebinda Aug 12 '15 at 12:58
  • @hebinda: Sure, but that should be an explicit decision. I have the impression that @Randoms really wanted a `[String : String]` instead of a `[String : String?]`. – Tali Aug 12 '15 at 13:31