7

I'm trying to use the Swift Decimal Structure for currency operations but I cannot format it.

How can I format var myDecimal:Decimal = 9999.99 to display $9,999.99?

Without using Decimals I can do it as follow...

let myTotal:NSNumber = 9999.99

let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true

currencyFormatter.numberStyle = .currency
currencyFormatter.locale = NSLocale.current
let priceString = currencyFormatter.string(from: myTotal)

myLabel.text = priceString

This works fine but I have been reading and Decimalseem to be the right type for currency.

I tried...

let myTotal:Decimal = 9999.99

let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true

currencyFormatter.numberStyle = .currency
// localize to your grouping and decimal separator
currencyFormatter.locale = NSLocale.current
let priceString = currencyFormatter.string(from: NSNumber(myTotal))

myLabel.text = priceString

... but I get error

Argument labels '(_:)' do not match any available overloads

What is the right way to format Decimals in Swift?

fs_tigre
  • 10,650
  • 13
  • 73
  • 146
  • 2
    Just use `string(for: myTotal)` instead of `string(from:` – Leo Dabus Sep 08 '17 at 00:57
  • 1
    Check this https://stackoverflow.com/questions/29782982/how-to-input-currency-format-on-a-text-field-from-right-to-left-using-swift/29783546?s=1|0.0625#29783546 – Leo Dabus Sep 08 '17 at 01:03
  • 1
    I don't think you'll see any benefit from using `Decimal` over `Float` or `Double` unless you need high precision, which is unlikely for currency. – Dave Wood Sep 08 '17 at 01:11
  • 1
    @LeoDabus `string(for: myTotal)` did the trick. Thanks. – fs_tigre Sep 08 '17 at 01:15
  • @DaveWood - What would you considered high precision, when would you use `Decimal`? – fs_tigre Sep 08 '17 at 01:17
  • @fs_tigre High precision is needed for things like 3D graphics, where you can have lots of digits after the decimal point. If you're only using 2 decimal places, you can avoid complicating your code and just use `Float`s. – Dave Wood Sep 08 '17 at 01:27
  • @fs_tigre you are welcome. Note that the currencyField of the linked question can be used with any currency including the ones that doesn't have any fraction digits at all. – Leo Dabus Sep 08 '17 at 02:07
  • 1
    *Never* use float or double for currency. See https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency – magma Oct 21 '20 at 22:01

1 Answers1

9

You can just cast your Decimal to an NSDecimalNumber first:

let priceString = currencyFormatter.string(from: myTotal as NSDecimalNumber)

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Charles Srstka
  • 16,665
  • 3
  • 34
  • 60
  • 1
    `string(from:)` is more specific, since it takes a number type as an argument rather than `Any`. – Charles Srstka Sep 08 '17 at 01:14
  • `NSDecimalNumber` *is* an `NSNumber` type. – Charles Srstka Sep 08 '17 at 01:17
  • 1
    `string(for:)` is a method on the abstract `Formatter` superclass. It is a nonspecific API that applies to any `Formatter` subclass, and the documentation says nothing about what it does when used on a `NumberFormatter` instance. `string(from:)` is actually mentioned in the `NumberFormatter` documentation, and what it does is actually documented. Therefore, I prefer it. In practice, it probably doesn't strictly matter since in all likelihood, one of these methods probably just calls through to the other. – Charles Srstka Sep 08 '17 at 01:21
  • They used `Any` because the method is on the abstract superclass, `Formatter`, and thus has no idea what types of objects it's going to take, or even whether it's a number formatter, date formatter, a person name formatter, some crazy custom formatter, or something else. They actually *did* add a method for each type `Double`, `Float`, `Decimal`, in a sense, because they added a method for `NSNumber`, which is the Objective-C type that all those types bridge to. – Charles Srstka Sep 08 '17 at 01:53