Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guard not unwrapping optional

Tags:

swift

optional

I'm trying to process a JSON object, using a guard statement to unwrap it and cast to the type I want, but the value is still being saved as an optional.

guard let json = try? JSONSerialization.jsonObject(with: data) as? [String:Any] else {
    break
}

let result = json["Result"]
// Error: Value of optional type '[String:Any]?' not unwrapped

Am I missing something here?

like image 948
John Montgomery Avatar asked Mar 30 '17 16:03

John Montgomery


People also ask

How do you force unwrap optional?

Optional binding is a construct that lets you safely access the optional's value. If you're absolutely certain an optional contains a value, you can take a shortcut by force unwrapping the value stored in the optional. To force unwrap an optional, we append an exclamation mark to the name of the variable or constant.

How do you unwrap optional type?

A common way of unwrapping optionals is with if let syntax, which unwraps with a condition. If there was a value inside the optional then you can use it, but if there wasn't the condition fails. For example: if let unwrapped = name { print("\(unwrapped.

How do I force unwrap in Swift?

Even though Swift isn't sure the conversion will work, you can see the code is safe so you can force unwrap the result by writing ! after Int(str) , like this: let num = Int(str)! Swift will immediately unwrap the optional and make num a regular Int rather than an Int? .

How many ways unwrap optional Swift?

You can unwrap optionals in 4 different ways: With force unwrapping, using ! With optional binding, using if let. With implicitly unwrapped optionals, using !


1 Answers

try? JSONSerialization.jsonObject(with: data) as? [String:Any]

is interpreted as

try? (JSONSerialization.jsonObject(with: data) as? [String:Any])

which makes it a "double optional" of type [String:Any]??. The optional binding removes only one level, so that json has the type [String:Any]?

The problem is solved by setting parentheses:

guard let json = (try? JSONSerialization.jsonObject(with: data)) as? [String:Any] else {
    break
}

And just for fun: Another (less obvious?, obfuscating?) solution is to use pattern matching with a double optional pattern:

guard case let json?? = try? JSONSerialization.jsonObject(with: data) as? [String:Any] else {
    break
}
like image 93
Martin R Avatar answered Oct 17 '22 07:10

Martin R