8

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?

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
John Montgomery
  • 6,739
  • 9
  • 52
  • 68
  • 1
    You must set parentheses: `(try? JSONSerialization.jsonObject(with: Data())) as? [String:Any]` – I think this has been asked and answered before, but haven't found the duplicate yet, so I might be wrong. – Martin R Mar 30 '17 at 16:12
  • http://stackoverflow.com/a/43121890/2303865 – Leo Dabus Mar 30 '17 at 16:19
  • @LeoDabus: I can not see any `try?` in that *question,* or a similar error message. – Martin R Mar 30 '17 at 16:20
  • `init?(data: Data) { guard let json = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] else { return nil } self.init(dictionary: json) }` – Leo Dabus Mar 30 '17 at 16:22

1 Answers1

13
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
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • That fixed the problem, seems weird that it doesn't unwrap it either way though. Isn't the point of a guard to guarantee that it has a value? – John Montgomery Mar 30 '17 at 17:21
  • 1
    @JohnMontgomery: For a "nested optional" of type `T??` optional binding checks if the expression has a value (of type `T?`), but that value can be `nil` :) Compare http://stackoverflow.com/questions/27225232/two-or-more-optionals-in-swift. – Martin R Mar 30 '17 at 17:30
  • I think I understand the difference there. Just to make sure: with no parentheses, the `try?` is on the outside, and the `guard` guarantees that it's returning a value but the value is still another optional (which in some cases may be important to distinguish from not returning a value at all). On the other hand, with the parentheses, the `as?` is on the outside, and since it's trying to cast to a non-optional type the value is non-optional. – John Montgomery Mar 30 '17 at 18:40
  • @JohnMontgomery: I would put is a bit differently. `try?` always adds a level of optionality to the expression that it is applied to. On the other hand, a conditional cast `expr as? T` evaluates to `T?`, even if `expr` already is an optional, so it does not add another level. – Martin R Mar 31 '17 at 07:55
  • @derpoliuk: Thanks for the edit suggestion. However, I would suggest that you post your solution as a separate answer. – Martin R Jun 15 '18 at 12:28