Search code examples
swiftcodablensdecimalnumberjsondecoder

How would I decode a NSDecimalNumber without loss of precision?


is there any way to tell JSONDecoder to convert incoming decimals to Strings?

 public struct Transaction: Decodable
 {
    public let total: NSDecimalNumber?


    enum CodingKeys: String, CodingKey {
        case total = "AMOUNT"

    }

    public init(from decoder: Decoder) throws
    {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let total = try values.decode(Decimal.self, forKey: .total)
        self.total = NSDecimalNumber(decimal: total);
    }

 }

Consider what would happen when AMOUNT is something like 397289527598234759823475455622342424358363514.42

I suppose with the code that I have I'd get nonConformingFloatDecodingStrategy exception with no recourse to recover from that or loss of precision.

Struggles around idiotic swift Decimal are documented all over the s.o. in particular here:

Making NSDecimalNumber Codable


Solution

  • The issue is that JSONDecoder is just a wrapper around JSONSerialization, which decodes decimal numbers into Double internally and only after converts them to Decimal. Sadly, unless you create your own JSON decoder, you cannot get around this issue when using numeric decimals from JSON.

    The only workaround currently possible is to change your backend to send all decimal numbers as Strings and then convert those into Decimals after decoding them as Strings.

    For more information, have a look at this open Swift bug: SR-7054.