2

Up until now, I've been unwrapping Optionals in Swift 2.1 like so:

@IBOutlet var commentTextView: UITextView!

if let comment = user["comment"] as? String {
    commentTextView.text = comment
}

I never really thought about it, but I think the reason I was doing this was because I was worried that this statement would throw an error if user["comment"] returned something other than a String:

commentTextView.text = user["comment"] as? String

If user["comment"] isn't a String, will the variable on the left of the assignment operator be assigned and throw an error or will the assignment be skipped?

3 Answers 3

1

I guess user is in fact a dictionary [String: Any] and what you really do with if let comment = user["comment"] as? String { ... } is not just unwrapping the optional but a conditional type casting (and then unwrapping an optional result of it):

Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.

Now, to answer your question, if user["comment"] isn't a String then the result will be that commentTextView.text will be assigned nil value, which is bad because its type is String! (implicitly unwrapped optional) about which we hold a promise that it will never be nil. So, yes, there will be an error, an exception actually, but not at the place you would like it to be but at the moment your application will try to access its value assuming that it's not going to be nil.

What you should really do depends on a particular case.

E.g. if you can make user to be a dictionary like [String: String], then you would be able to truly get to unwrapping the optionals and use something like if let comment = user["comment"] { ... }. Or, if you are totally sure that the value for "comment" key will always be there, then you could just do let comment = user["comment"]!.

But if that's not possible then you have to stick with down-casting and the only other thing you can do is to use forced form of it, that is commentTextView.text = user["comment"] as! String. This one at least will produce an exception right at the spot in case if the value at "comment" happens to be not a String but something else.

Sign up to request clarification or add additional context in comments.

Comments

0

nil will be assigned to the variable.

If the type of the variable is a non-optional, you'll get a runtime error.

However if user["comment"] is a String you'll get a compiler error about missing ! or ?.

4 Comments

No, in an if let, nil is never assigned to the variable.
The question is about optional chaining which is the second snippet, the assignment expression
No, you will not get a runtime error, just try it. That's what the ? operator is about. ? means "could", ! means "should" and triggers a runtime error if it's not.
Ah, I see! Makes sense.
0

First we need to know of what type the dictionary "user" is.

I assume it is of an unknown type like [String: AnyObject], otherwise why would you try to unwrap it as an String. Let us write a short test to see what happens:

let dict: [String: AnyObject] = ["SomeKey" : 1]
if let y = dict["SomeKey"] as? String {
    print(y)
}

You can see clearly that the value of "SomeKey" is an Integer. Trying to unwrap it as an String triggers no error, the "if" statement is just skipped. If an assignment actually happened is hard to prove (maybe by looking at the assembler code) because the variable "y" simply does not exist after the if statement. I assume it will not be created at all.

If the type of the dictionary is known as [String: String] you can omit the try to unwrap it as a String because it's always clear that the type is String.

let dict2: [String: String] = ["SomeKey" : "SomeValue"]
if let y = dict2["WrongKey"] {
    // In this case print(y) will not be called because the subscript operator of the dictionary returns nil
    print(y)
}

// In this case print(y) will be called because the key is correct
if let y = dict2["SomeKey"] {
    print(y)
}

2 Comments

The OP is asking about optional chaining not optional binding.
The title is wrong. This is optional chaining: developer.apple.com/library/ios/documentation/Swift/Conceptual/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.