Is it possible to extend NSDecimalNumber to conform Encodable & Decodable protocols?
Asked
Active
Viewed 5,525 times
12
Lorenzo B
- 33,456
- 23
- 115
- 188
Arda Keskiner
- 742
- 7
- 23
-
What have you tried so far? What are your requirements? What are you trying to encode / decode? – Lorenzo B Sep 19 '17 at 07:45
-
@LorenzoB I checked Apple's documentation https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types for encoding and decoding custom types. I'm trying to parse responses from server which I'd loose precision if I used Double. – Arda Keskiner Sep 19 '17 at 07:54
3 Answers
13
It is not possible to extend NSDecimalNumber to conform to Encodable & Decodable protocols. Jordan Rose explains it in the following swift evolution email thread.
If you need NSDecimalValue type in your API you can build computed property around Decimal.
struct YourType: Codable {
var decimalNumber: NSDecimalNumber {
get { return NSDecimalNumber(decimal: decimalValue) }
set { decimalValue = newValue.decimalValue }
}
private var decimalValue: Decimal
}
Btw. If you are using NSNumberFormatter for parsing, beware of a known bug that causes precision loss in some cases.
let f = NumberFormatter()
f.generatesDecimalNumbers = true
f.locale = Locale(identifier: "en_US_POSIX")
let z = f.number(from: "8.3")!
// z.decimalValue._exponent is not -1
// z.decimalValue._mantissa is not (83, 0, 0, 0, 0, 0, 0, 0)
Parse strings this way instead:
NSDecimalNumber(string: "8.3", locale: Locale(identifier: "en_US_POSIX"))
Wojciech Nagrodzki
- 2,708
- 14
- 12
3
In swift you should use Decimal type. This type confirms to protocols Encodable & Decodable from the box.
If you have NSDecimalNumber type in your code it's easy to cast it to Decimal
let objcDecimal = NSDecimalNumber(decimal: 10)
let swiftDecimal = (objcDecimal as Decimal)
Nick Rybalko
- 182
- 1
- 5
0
With Swift 5.1 you can use property wrappers to avoid the boilerplate of writing a custom init(from decoder: Decoder) / encode(to encoder: Encoder).
@propertyWrapper
struct NumberString {
private let value: String
var wrappedValue: NSDecimalNumber
init(wrappedValue: NSDecimalNumber) {
self.wrappedValue = wrappedValue
value = wrappedValue.stringValue
}
}
extension NumberString: Decodable {
init(from decoder: Decoder) throws {
value = try String(from: decoder)
wrappedValue = NSDecimalNumber(string: value)
}
}
extension NumberString: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(wrappedValue.stringValue)
}
}
extension NumberString: Equatable {}
Usage:
struct Foo: Codable {
@NumberString var value: NSDecimalNumber
}
boa_in_samoa
- 487
- 7
- 15