Search code examples
swifterror-handlingswxmlhash

Swift: Error ignores try statement


In my code I'm deserialising some XML using the SWXMLHash library. Its .value() method has the throws keyword in its declaration, as do any custom deserialise functions.

I have the following line of code:

let myValue : UInt8 = try? xml["Root"]["ValueNode"].value()

Since the library doesn't include a deserialiser for UInt8, I've defined my own:

extension UInt8: XMLElementDeserializable {
    public static func deserialize(_ element: XMLElement) throws -> UInt8 {
        return UInt8(element.text)!
    }
}

This works when the node has a value. However, when the node doesn't exist or is nil, an error occurs on the following line:

return UInt8(element.text)!  // Fatal error: Unexpectedly found nil while unwrapping an Optional value

This is supposed to happen, obviously. What I don't understand is why this error is not being caught by my try? statement and returning nil instead of throwing that error.

Can anyone help?


Solution

  • Not all errors can be caught in Swift. If you mark a function using the throws keyword it indicates that the function might throw a recoverable error. However, your custom implementation doesn't actually throw any errors. Only errors thrown from functions marked with the throws keyword and thrown using the code throw Error can be caught by a do-catch block.

    try? is a way to convert a throwable function's return value to an optional. If the function would throw an error, the value after the try? will be nil, otherwise it will be an optional value.

    When you use the !, you specifically tell the compiler that you know what you are doing and if the operation on which you used the ! fails, your app shouldn't fail gracefully.

    You'll need to change your deserialize method to handle the optional unwrapping gracefully or throw and error.

    extension UInt8: XMLElementDeserializable {
        public static func deserialize(_ element: XMLElement) throws -> UInt8 {
            if let val = UInt8(element.text) {
                return val
            } else {
                throw NSError(domain: "Couldn't deserialize value to UInt8", code: 1)
            }
        }
    }