To avoid the floating point issues we can either use a type String or Decimal for the keys price and amount. In either case we can not decode directly into either type but we first need to use the given type which is Double so we need a custom init for this.
First case is to use String (I see no reason to use optional fields as default, change this if any of the fields actually can be nil)
struct StockNFS: Codable {
let name: String
let price: String
let no: Int
let id: String
let amount: String
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
let priceValue = try container.decode(Double.self, forKey: .price)
price = "\(priceValue.roundToDecimal(8))"
//... rest of the values
}
}
The rounding is used with a method inspired from this excellent answer
extension Double {
func roundToDecimal(_ fractionDigits: Int) -> Double {
let multiplier = pow(10, Double(fractionDigits))
return (self * multiplier).rounded() / multiplier
}
}
To do the same but with the numeric type Decimal
we do
struct StockNFS2: Codable {
let name: String
let price: Decimal
let no: Int
let id: String
let amount: Decimal
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
let priceValue = try container.decode(Double.self, forKey: .price)
price = Decimal(priceValue).round(.plain, precision: 8)
//... rest of the values
}
}
Again the rounding method was inspired from the same answer
extension Decimal {
func round(_ mode: Decimal.RoundingMode, precision: Int = 2) -> Decimal {
var result = Decimal()
var value = self
NSDecimalRound(&result, &value, precision, mode)
return result
}
}