Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Under what circumstances does JSONSerialization.data(withJSONObject:) throw a catchable error?

JSONSerialization.data(withJSONObject:options:) (aka dataWithJSONObject in Swift 2) is declared throws. However, passing invalid objects causes a crash, not a catchable error:

do  {
    // Crash
    try JSONSerialization.data(
        withJSONObject: NSObject(),
        options: [])
    }
catch
    {
    // Never reached
    print("Caught error:", error)
    }

Why is that method declared “throws,” then? Under what circumstances does it throw an exception?

Not knowing what causes an error to be thrown makes it hard to know how to handle the error, and makes it impossible to write tests that verify that handling.

like image 463
Paul Cantrell Avatar asked Nov 07 '15 15:11

Paul Cantrell


1 Answers

Turns out it’s the same situation as this question: you can create a Swift string that contains invalid unicode (what?!), and that causes an exception.

let bogusStr = String(
    bytes: [0xD8, 0x00] as [UInt8],
    encoding: String.Encoding.utf16BigEndian)!

do  {
    let rawBody = try JSONSerialization.data(
        withJSONObject: ["foo": bogusStr], options: [])
    }
catch
    {
    // Exception lands us here
    print("Caught error:", error)
    }

Why does the example code in the original question crash, then, instead of also throwing an error?

Replying to a bug report, Apple informed me that you should call JSONSerialization.isValidJSONObject(_:) before data(withJSONObject:) if you don’t know for sure that the object is encodable, failing to do that is a misuse of the API, and that’s why they decided it should crash instead of throwing something catchable.

like image 62
Paul Cantrell Avatar answered Nov 08 '22 16:11

Paul Cantrell