41

I am trying to use NumberFormatter with Swift 3 Decimal, but I'm confused as to how Decimal is really being implemented. The problem I'm having is that Decimal is a struct, so I have to bridge it to an NSDecimalNumber every time I want to use a formatter, which I'd like to avoid.

let formatter = NumberFormatter()
formatter.numberStyle = .decimal
let decimal = Decimal(integerLiteral: 3)
let string = formatter.string(from: decimal as NSDecimalNumber)

Is the ideal workaround for this to implement my own extension that takes a Decimal?

extension NumberFormatter {
    open func string(from number: Decimal) -> String? {
        return string(from: number as NSDecimalNumber)
    }
}

More generally, every time I need to pass in an object type am I going to need to bridge Decimal or write more extensions?

EDIT

I guess I'm wondering more generally about NSDecimal Decimal and NSDecimalNumber in Swift 3. It's not clear to me at all what's going on here. Should I be replacing NSDecimalNumber with Decimal for Swift 3? The docs write:

The Swift overlay to the Foundation framework provides the Decimal structure, which bridges to the NSDecimalNumber class. The Decimal value type offers the same functionality as the NSDecimalNumber reference type, and the two can be used interchangeably in Swift code that interacts with Objective-C APIs. This behavior is similar to how Swift bridges standard string, numeric, and collection types to their corresponding Foundation classes.

Which at first I thought meant that Decimal was the new NSDecimalNumber like Error is the new NSError - but now I'm not so sure. That also says 'Decimal value type offers the same functionality as the NSDecimalNumber reference type` - is this really true? I can't seem to get much of the same functionality (without bridging it first, of course, is that what they mean?). I have found a few posts and a bit of info here and there, but nothing that convincing. Does anyone have any knowledge or insight?

My app specifically is using NSDecimalNumber for currency and formatting, so rounding and formatting are a high priority.

Alex
  • 3,861
  • 5
  • 28
  • 44
  • 2
    As far as I can see, `NumberFormatter(style: .decimal)` does not compile, moreover, `NumberFormatter()` does not create an optional. – Is that your real code, or am I overlooking something? – Martin R Dec 14 '16 at 17:47
  • `extension NumberFormatter { convenience init(style: NumberFormatter.Style) { self.init() self.numberStyle = style } func string(from decimal: Decimal) -> String? { return string(from: decimal as NSNumber) } }` – Leo Dabus Dec 14 '16 at 18:21
  • Yeah, that's my real code and it compiles fine. I'm on Xcode 8.1, has this changed in 8.2 or something? – Alex Dec 14 '16 at 20:02
  • Doesn't compile for me in Xcode 8.1, because there's no such initializer. – Tom Harrington Dec 15 '16 at 17:21
  • sorry, that was a simple NSNumberFormatter extension we have, the code has been updated – Alex Dec 15 '16 at 17:28

1 Answers1

41

I asked my question on the swift-users board and got a wonderful answer, check it out here: https://lists.swift.org/pipermail/swift-users/Week-of-Mon-20161219/004220.html

Text version of the response:

Swift 3 introduced the Decimal class, which at first I thought was supposed to 'replace' NSDecimalNumber in Swift, like Error is the new NSError, etc. However, upon further investigation, it's not clear to me that this is actually the case. It appears that Swift 3's Decimal is more just an NSDecimal, but with some bridging so it can use NSDecimalNumber's methods when needed. What I'm wondering is, should I be replacing NSDecimalNumber in my app with Decimal? Is Decimal the successor to NSDecimalNumber in Swift?

Decimal is actually a struct, not a class, and in fact it's just the Swift name for the NSDecimal struct in Objective-C. In Swift, functions like NSDecimalAdd are exposed as operators on Decimal and are used to satisfy the requirements of various numeric protocols like SignedNumber.

The end result is that Decimal is a lot like Int or Double—it's a value type which can be conveniently used to do all sorts of math. NSDecimalNumber, then, is equivalent to NSNumber.

Since my app is doing a lot of formatting, it would require much casting between Decimal and NSDecimalNumber, or adding of extensions to use, so I don't think it would be natural/convenient without many extensions. Is Swift planning to improve Decimal to be more usable with NumberFormatter, or other object-oriented number APIs? Hopefully I have given enough information to answer my question.

Objective-C APIs which use NSDecimalNumber should be bridged to Decimal, just as NSString is bridged to String. That bridging may not work for category methods, though; you could expose those to Swift by writing an extension on Decimal which casts self to NSDecimalNumber and then calls through to your Objective-C methods.

Similarly, you should be able to use Decimal with NumberFormatter by casting to NSDecimalNumber, just as you would cast Int or Double to NSNumber to use them with NumberFormatter.

Alex
  • 3,861
  • 5
  • 28
  • 44
  • 1
    what is the advantage of using it over `Double` type? – Siempay Aug 15 '17 at 12:44
  • 9
    @brahimm You should never use Double/Float to represent Currency. You can read a nice explanation here: https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency – Caio Sep 30 '17 at 18:51
  • @Caio I have the same concern, but I'm now also concerned that Swift's `Decimal` doesn't address this :/ – Ky - May 29 '20 at 23:37