2

I have this code in Swift:

 var dictionary: [String: Any?]? 

 fileprivate func value(forKey key: Key) -> Any? {
    if let dictionary = self.dictionary {
        return dictionary[key.rawValue]
    }

     ...
 }

I get a warning "Expression implicitly coerced from 'Any??' to 'Any?' in the return statement ". What am I doing wrong?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Deepak Sharma
  • 5,577
  • 7
  • 55
  • 131
  • Why is `dictionary` declared as `[String: Any?]?` instead of `[String: Any]?` ? In other words, why is the value an optional? Is there a good use case for that with a dictionary? – rmaddy Jul 11 '19 at 16:17
  • Well, does Any covers nil values? If yes, there is no need for 'Any?'. Regardless, why is the warning. What is 'Any??' ? – Deepak Sharma Jul 11 '19 at 16:23
  • Are you trying to distinguish between `nil` in the dictionary versus the entry being missing entirely? (`Any??` is an `Optional>`; it can hold values like `.some(nil)`). These kinds of types are extremely difficult to work with. Can your dictionary really hold absolutely *any* type? If I put a CBPeripheral or UIApplication in there, it would be ok? Or is it really some subset of types, and you don't mean "Any" at all? – Rob Napier Jul 11 '19 at 16:28
  • According to the language semantics a dictionary `value` is `nil` if the key does not exist. So declaring the value as optional type is pointless. – vadian Jul 11 '19 at 16:29
  • Similarly, do you really mean to distinguish between an empty dictionary (`[:]`) and a missing dictionary (`nil`)? An optional dictionary is harder to work with, and generally isn't necessary. – Rob Napier Jul 11 '19 at 16:31
  • The dictionary is really an optional in my case, it may be there or it may not be there. How does empty dictionary [:] help anyways? – Deepak Sharma Jul 11 '19 at 16:35
  • 1
    Why not simply `let value = dictionary?["key"] ?? ""` ?? – Shehata Gamal Jul 11 '19 at 16:36
  • 1
    @Sh_Khan But falling back to some "special value" is exactly what optionals are there to prevent. You often need to know the difference between no value and the empty string. What you suggest is not a good general solution. It may be a valid solution in specific cases, but not in general. – rmaddy Jul 11 '19 at 16:53
  • @DeepakSharma If you were passed nil, how would your program behave differently than if you were passed and empty dictionary? In both cases, all keys will return nil. So unless you have some behavior that is different for the dictionary being nil, you should just use an empty dictionary and make it non-optional. This will significantly simplify the code. (Basically, what is in the `...` at the end of your function, and how does it differ from a dictionary that does not contain `key`?) – Rob Napier Jul 11 '19 at 16:55
  • @rmaddy this depends on how op wants it whether a default value or surround the above line with `guard` it still one line of code no need for a function – Shehata Gamal Jul 11 '19 at 16:56
  • 1
    @RobNapier If dictionary was nil, I would fetch values from NSUserDefaults. That's the behavior of program. – Deepak Sharma Jul 11 '19 at 18:47

4 Answers4

7

The value of your dictionary is Any?. The result of accessing a dictionary value is an optional because the key might not exist. So you end up with Any??.

There's really no need for your dictionary to be declared to have optional values.

Change it to [String: Any]? and your issue goes away.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
6

Nobody seemed to actually explain the error message, so I'll go ahead and do that.

Starting with a simple Int

Suppose you had an integer, i, which I'll represent it as this diagram:

let i = 123
// ┏━━━━━━━━━━━━┓
// ┃ Name: i    ┃
// ┃ Type: Int  ┃
// ┠────────────┨
// ┃ Value: 123 ┃
// ┗━━━━━━━━━━━━┛

Any

You can "box up" i into a value of type Any. This container "hides" the Intness of i.

  • This has a benefit of allowing you to handle it alongside other type-erased values, like "s" or true.
  • ...but it has a down side: there really isn't much you can do with a value of type Any. Because Any is so broad, it doesn't really describe much that you can do with the value. You can't .lowercased() it like a String, you can't toggle() it, like a Bool, all you can really do is pass it around, and eventually down-cast it into another type which can do more things.

When you box up i into an Any, it looks something like this:

let any123 = i as Any
// ┏━━━━━━━━━━━━━━━━━━┓
// ┃ Name: any123     ┃
// ┃ Type: Any        ┃
// ┠──────────────────┨
// ┃ Value:           ┃
// ┃  ┏━━━━━━━━━━━━┓  ┃
// ┃  ┃ Name: i    ┃  ┃
// ┃  ┃ Type: Int  ┃  ┃
// ┃  ┠────────────┨  ┃
// ┃  ┃ Value: 123 ┃  ┃
// ┃  ┗━━━━━━━━━━━━┛  ┃
// ┗━━━━━━━━━━━━━━━━━━┛

Any is "opaque" to the type system, as in, the type system cannot determine what lies beyond the Any box border (which is why I represent it with a solid outline).

Optional<T>

Optional is a generic enum with two cases, some (which stores an associated value called wrapped) and none (which doesn't). It's generic over any type T, but for the purposes of this example, I'll talk only about the case where T is Int, so Optional<Int>, a.k.a. Int?.

nil is a shorthand for saying Optional<T>.none, for any type T.

let some123: Int? = 123 // implicitly wraps the value into an optional
let none: Int? = nil

// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓    ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
// ╏ Name: some123    ╏    ╏ Name: none    ╏
// ╏ Type: Int        ╏    ╏ Type: Int?    ╏
// ┠──────────────────┨    ┠───────────────┨
// ╏ Case: .some      ╏    ╏ Case: .none   ╏
// ╏ wrapped:         ╏    ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
// ╏  ┏━━━━━━━━━━━━┓  ╏
// ╏  ┃ Name: i    ┃  ╏
// ╏  ┃ Type: Int  ┃  ╏
// ╏  ┠────────────┨  ╏
// ╏  ┃ Value: 123 ┃  ╏
// ╏  ┗━━━━━━━━━━━━┛  ╏
// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛

Unlike Any, optional is "transparent" to the type system (which is why I represent it with a dashed outline). You can always see the type of the value within.

Composition of Optionals

Optionals can compose with each other, acting like Russian nesting dolls:


let someSome123: Int?? = 123        // Shorthand for Optional<Optional<Int>>.some(Optional<Int>.some(123))
let someOfNone: Int??  = .some(nil) // Shorthand for Optional<Optional<Int>>.some(Optional<Int>.none)
let none: Int??        = nil        // Shorthand for Optional<Optional<Int>>.none

// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓    ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓    ┏╍╍╍╍╍╍╍╍╍╍╍╍╍┓
// ╏ Name: someSome123      ╏    ╏ Name: someOfNone  ╏    ╏ Name: none  ╏
// ╏ Type: Int??            ╏    ╏ Type: Int??       ╏    ╏ Type: Int?? ╏
// ┠────────────────────────┨    ┠───────────────────┨    ┠─────────────┨
// ╏ Case: .some            ╏    ╏ Case: .some       ╏    ╏ Case: .none ╏
// ╏ wrapped:               ╏    ╏ wrapped:          ╏    ┗╍╍╍╍╍╍╍╍╍╍╍╍╍┛
// ╏  ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓  ╏    ╏  ┏╍╍╍╍╍╍╍╍╍╍╍╍╍┓  ╏
// ╏  ╏ Type: Int?       ╏  ╏    ╏  ╏ Type: Int?  ╏  ╏
// ╏  ┠──────────────────┨  ╏    ╏  ┠─────────────┨  ╏
// ╏  ╏ Case: .some      ╏  ╏    ╏  ╏ Case: .none ╏  ╏
// ╏  ╏ wrapped:         ╏  ╏    ╏  ┗╍╍╍╍╍╍╍╍╍╍╍╍╍┛  ╏
// ╏  ╏  ┏━━━━━━━━━━━━┓  ╏  ╏    ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛
// ╏  ╏  ┃ Type: Int  ┃  ╏  ╏
// ╏  ╏  ┠────────────┨  ╏  ╏
// ╏  ╏  ┃ Value: 123 ┃  ╏  ╏
// ╏  ╏  ┗━━━━━━━━━━━━┛  ╏  ╏
// ╏  ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛  ╏
// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛   

This happens for example, when you try to subscript a Dictionary that stores T? values. There are 3 possible outcomes:

  1. A value exists for the key, and the value is non-nil. This is like the constant someSome123 above
  2. A value exists for the key, but the value is nil. This is like the constant someOfNone above
  3. No value exists for the key. This is like the constant none above

Many languages allow all types to store a null value. This is a weaker design, because it can't distinguish between #2 and #3. "Does this null mean the value is absent, or that a value is there, but the value itself is null?" The nestability of optionals allows for that distinction . There are many other contexts in which it can come up, like accessing the first value of an Array<Optional<T>>.

Composing Any and Optional

Just like how optionals can contain optionals, optionals can contain values of type Any, and vice versa. Here are some example that recreates your error:

let any123: Any = 123
let optionalOptionalAny123: Any?? = any123
let optionalAny123: Any? = optionalOptionalAny // ⚠️ warning: expression implicitly coerced from 'Any??' to 'Any?'

// ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓    ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓
// ╏ Name: optionalOptionalAny123  ╏    ╏ Name: optionalAny123             ╏
// ╏ Type: Optional<Optional<Any>> ╏    ╏ Type: Optional<Any>              ╏
// ┠───────────────────────────────┨    ┠──────────────────────────────────┨
// ╏ Case: .some                   ╏    ╏ Case: .some                      ╏
// ╏ wrapped:                      ╏    ╏ wrapped:                         ╏
// ╏   ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓   ╏    ╏  ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓  ╏
// ╏   ╏ Type: Optional<Any>   ╏   ╏    ╏  ┃ Type: Any                  ┃  ╏
// ╏   ┠───────────────────────┨   ╏    ╏  ┠────────────────────────────┨  ╏
// ╏   ╏ Case: .some           ╏   ╏    ╏  ┃ Value:                     ┃  ╏
// ╏   ╏ wrapped:              ╏   ╏    ╏  ┃   ┏╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┓  ┃  ╏
// ╏   ╏  ┏━━━━━━━━━━━━━━━━━┓  ╏   ╏    ╏  ┃   ╏ Name: some123       ╏  ┃  ╏
// ╏   ╏  ┃ Name: any123    ┃  ╏   ╏    ╏  ┃   ╏ Type: Optional<Int> ╏  ┃  ╏
// ╏   ╏  ┃ Type: Any       ┃  ╏   ╏    ╏  ┃   ┠─────────────────────┨  ┃  ╏   
// ╏   ╏  ┠─────────────────┨  ╏   ╏    ╏  ┃   ╏ Case: .some         ╏  ┃  ╏   
// ╏   ╏  ┃  Value:         ┃  ╏   ╏    ╏  ┃   ╏ wrapped:            ╏  ┃  ╏   
// ╏   ╏  ┃  ┏━━━━━━━━━━━┓  ┃  ╏   ╏    ╏  ┃   ╏    ┏━━━━━━━━━━━┓    ╏  ┃  ╏   
// ╏   ╏  ┃  ┃ i: Int    ┃  ┃  ╏   ╏    ╏  ┃   ╏    ┃ i: Int    ┃    ╏  ┃  ╏   
// ╏   ╏  ┃  ┠───────────┨  ┃  ╏   ╏    ╏  ┃   ╏    ┠───────────┨    ╏  ┃  ╏   
// ╏   ╏  ┃  ┃ Value: 123┃  ┃  ╏   ╏    ╏  ┃   ╏    ┃ Value: 123┃    ╏  ┃  ╏   
// ╏   ╏  ┃  ┗━━━━━━━━━━━┛  ┃  ╏   ╏    ╏  ┃   ╏    ┗━━━━━━━━━━━┛    ╏  ┃  ╏   
// ╏   ╏  ┗━━━━━━━━━━━━━━━━━┛  ╏   ╏    ╏  ┃   ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛  ┃  ╏   
// ╏   ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛   ╏    ╏  ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛  ╏   
// ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛    ┗╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍╍┛   

As you can see, both optionalOptionalAny123 and optionalAny123 contain two layers of optionality, and one layer of type erasure (as provided by Any). This warning exists to tell you that by coersing Any?? to Any?, you're obscuring the second layer of optionality behind the hidden veil of Any.

To get around this warning, you can follow one of its three suggestions:

  1. provide a default value to avoid this warning

    Peel back one layer of optionality by unwrapping its contents, or resorting to the default value if it's nil. This is rarely a good idea, because sensible default values rarely exist.

    let optionalAny123: Any? = optionalOptionalAny ?? 0
    
  2. force-unwrap the value to avoid this warning

    Peel back one layer of optionality by forcefully unwrapping it. This is rarely a good idea, because it requires you be absolutely certain you don't have a nil.

    let optionalAny123: Any? = optionalOptionalAny!
    
  3. explicitly cast to 'Any?' with 'as Any?' to silence this warning

    You can keep the value as-is (with 2 layers of optionality), hiding one of the layers of optionality behind the veil of Any. Adding as Any? doesn't actually do anything at runtime. It's syntactic salt that indicates that you intentionally wanted this behaviour, as opposed to mistakenly letting it slip by.

    let optionalAny123: Any? = optionalOptionalAny as Any?
    

Ideally, you should try to flatten optionality as soon as you can. If the nestedness of optional carries some sort of significance in your system, act upon that as soon as you can, and flatten the optional shortly thereafter.

Alexander
  • 59,041
  • 12
  • 98
  • 151
3

Since type of dictionary sub scripting value is Any? with dictionary always append ? to it's key/value access = Any??

you can replace

[String: Any?]? 

with

[String: Any]? 
Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
1

I want to add an example about what rmaddy said.

Suppose we have the following code:

var dictionary: [String: Any] = ["coco": 1, "foo": "yo"]

func value(dic: [String: Any] , forKey key: String) -> Any {
  // Expression implicitly coerced from 'Any?' to 'Any'
  return dictionary[key]
}

// Optional(1)
print(value(dic: dictionary, forKey: "coco"))

At the beginning, even if we don't declare any optional value, the dictionary don't know if the key really exists. So, you can better understand why you have another optional value that you didn't expect.

damdamo
  • 331
  • 2
  • 10