Documentation link
Why does the NumberFormatter
function func string(from number: NSNumber) -> String?
return a String?
rat
There's an ancient (2002) response on the Apple mailing list which might partially answer your question:
The strange behavior isn't NSNumber -- NSNumber -stringValue seems to be returning results one would expect. It's NSNumberFormatter which is returning unusual results.
The abstract problem here is that NSNumberFormatter was originally written to handle money amounts for EOF, so it doesn't deal with unusual numbers well (or general purpose string formatting for that matter).
The more concrete problem is that when you ask an NSNumberFormatter for -stringForObjectValue:, if the object is not an NSDecimalNumber, it converts it to an NSDecimalNumber using [NSDecimalNumber decimalNumberWithString:[objectValue stringValue]] which is not going to handle unusual values well, in addition to potentially other issues. A further issue, though I don't know this is the case, is that I don't think there is an NSDecimal (the struct) representation of positive and negative infinity, as there is for NaN.
I don't actually understand that response enough to say that it's why an optional is returned, but I suspect it's related.
There's example code in the original question, but none of it returns a nil
, it always gives a string.
I think it's fait to say that, if you try to format a common type (like Double) to String using a NumberFormatter, there is no way it would return nil
.
In my opinion, this is one of the few cases where force-unwrapped optionals make perfect sense.
func formatToDecimalNumber(_ value: Double) -> String {
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 2
formatter.locale = Locale.current
return formatter.string(from: NSNumber(value: value))!
}
Just for fun: Here is a (constructed, not real-world) example where
string(from:)
actually returns nil
:
let num = NSNumber(bytes: UnsafeRawPointer(bitPattern: 1)!, objCType: "v")
print(num) // <>
let fmt = NumberFormatter()
fmt.numberStyle = .decimal
let str = fmt.string(from: num)
print(str as Any) // nil
The reason is that this num
does not represent a number: It is created
using the NSValue
(from which its inherits) initializer
init(bytes:objCType:) with a value representing void
. ("v" is the type encoding for void
,
the pointer value is irrelevant.)