2

I'm using Swift's NumberFormatter with currency numberStyle. I set the following properties:

let value: Double = 345.23
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.minimumFractionDigits = 0
formatter.maximumFractionDigits = 0
formatter.locale = Locale(identifier: "en_US")
formatter.currencyCode = "USD"
print(formatter.string(from: NSNumber(value: value))) //Will print $345 as expected

If I change the currency and print again, I get two decimals. Why?

formatter.currencyCode = "EUR"
print(formatter.string(from: NSNumber(value: value))) //Will print €345.23 with two decimals

Is this supposed to happen or is it a bug? If I change the locale to something else, like "fr_FR", and then back again to "en_US" it will print with 0 decimals. Please see attached result from XCTestCase. Only when locale has been modified will the currency have the expected number of decimals.

CurrencyNumberFormatter test

UPDATE: It works for iOS 13.3, but fails for iOS 12.0 and 11.0.1

Akisha
  • 61
  • 1
  • 5
  • 1
    I cannot reproduce this. I get "€345" without fractional digits – Andreas Oetjen Feb 20 '20 at 19:55
  • Ok. Thanks for your feedback! That made me test on different OS, and I found that it worked for iOS 13.3, but fails for iOS 11.01 and iOS 12.0. – Akisha Feb 20 '20 at 22:09
  • You should set your Locale before setting the other properties. When you set the Locale it will set default values for many formatter attributes. **The locale determines the default values for many formatter attributes, such as ISO country and language codes, currency code, calendar, system of measurement, and decimal separator.** – Leo Dabus Feb 21 '20 at 00:53
  • Any reason for not using `Decimal` to represent your value? Relevant: https://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency – fphilipe Feb 21 '20 at 07:58
  • Thank you for your reply, Leo Dabus. Even though I set the locale first, the formatting fails as soon as I change the currencyCode. But as mentioned above, it only happens for iOS < 13. – Akisha Feb 21 '20 at 07:59
  • Thank you for your reply, fphilipe. I can't see why using Decimal would help me in this situation. Accuracy is not an issue, I just wan't those decimals to disappear :) I want to show large numbers in a compact form. For instance $845,675,333.45 will be displayed as $846M. But if I have just changed from another currency, it shows $845.68M instead for iOS < 13, and the string length is an issue for me. – Akisha Feb 21 '20 at 08:17

1 Answers1

1

If you execute your code without changing minimumFractionDigits and maximumFractionDigits you will notice that these setings are reinitialized with the defaults associated to each currency:

// My favourite test currencies with 2, 3, 2 and 0 decimals
for mycur in ["USD", "TND", "EUR", "JPY" ] {
    myformatter.currencyCode = mycur
    print ("cur:\(mycur): min:\(myformatter.minimumFractionDigits) max:\(myformatter.maximumFractionDigits) result:\(myformatter.string(from: NSNumber(value: value)) ?? "xxx")")
}

This code produces the following results:

cur:USD: min:2 max:2 result:$345.23
cur:TND: min:3 max:3 result:TND 345.230
cur:EUR: min:2 max:2 result:€345.23
cur:JPY: min:0 max:0 result:¥345

These settings are by the way initialized as soon as you select a currency style for your NumberFormatter with the currency settings of your default locale. And every change of locale might also change the currency, and these associated settings.

Now I cannot reproduce your problem with my currently installed versions: as soon as I change once minimumFractionDigits and maximumFractionDigits, it will keep the new settings regardless of any currency change. (This behavior is also independent of the settings of the current currency or the default settings).

But looking at your problem description, there is a very high probability that the NumberFormatter did not keep track in the previous version of the explicit change of the settings, and that it systematically reloaded the default currency settings at every currency change.

A version independent workaround is then to reaply youd ad-hoc settings everytime you change locale or currency.

Christophe
  • 68,716
  • 7
  • 72
  • 138