18

I know the definition for a single exclamation mark, but two?

I was coding today and the compiler "force" me to add one more ! to my sentence:

mySignal.subscribeNext({
        (value: AnyObject!) -> () in
        let list: RACSequence = value["myObject"]!!.rac_sequence
        ...

If I use only one ! mark, the project doesn't compile, giving me the error: "Value of optional type 'AnyObject?' not unwrapped; did you mean to use '!' or '?'?" Then I add one more ! and everything works.

What's the meaning for two exclamation marks in Swift?

FelipeDev.-
  • 3,113
  • 3
  • 22
  • 32

2 Answers2

9

You're storing an AnyObject! in the dictionary, so you're storing an optional as the value. Dictionary subscripts always return an optional of the value you're storing, so you're getting an optional optional, which is why you need two !, to unwrap it twice.

Dave Wood
  • 13,143
  • 2
  • 59
  • 67
  • 2
    The code does not "store an AnyObject! in the dictionary"-- the code subscripts into a value of type AnyObject!. It's not clear why subscripting into an AnyObject! is possible in the first place, but double optionals are probably not an intended outcome in any API (see my post below). – Rikki Gibson Feb 02 '16 at 19:56
  • 1
    I today experienced the same here: appDelegate.window!!.rootViewController = rootNavigationController so it does not seem to be related to dictionaries. – osxdirk Aug 31 '16 at 21:37
7

This is a strange artifact of the use of the AnyObject type instead of an explicit dictionary type. Normally, a monad like Optional (thanks user2864740) implements a bind operation with a signature like Optional<T>.bind(f: T -> Optional<U>) -> Optional<U>.

This makes it so when you access an optional member of an optional value, you don't get a double-optional that you have to unwrap like an onion with each layer of access.

If you do a similar example with an explicit dictionary, you'll find the resulting type to be just a single layer of Optional:

import UIKit

let anyDict: AnyObject? = ["foo" : 34, "bar" : 13]
let anyElement = anyDict?["foo"]
print(anyElement) // Optional(Optional(34))

let dict: [String : Int]? = ["foo" : 34, "bar" : 13]
let element = dict?["foo"]
print(element) // Optional(34)

It's not clear why this is happening with AnyObject, but I don't believe it's the intended behavior.

Rikki Gibson
  • 4,136
  • 23
  • 34
  • I have pasted this code in the Playground, but I am getting Cannot convert value of type [String: Int] to specified type AnyObject, when using Swift 2.0. – lmiguelvargasf Sep 01 '15 at 22:10
  • That's strange. This example was written based on Swift 1.2, but I got the same results with Swift 2 in Xcode 7 beta 6. – Rikki Gibson Sep 02 '15 at 02:18
  • Sorry, I forgot to add `import UIKit`, I guess you should include this line at the beginning of your snippet, and it would be great if you change `println` to `print`. – lmiguelvargasf Sep 02 '15 at 20:21