1

I am working with currencies in Swift. I need to convert a String to Decimal. There are few problems which I think I have solved:

  • decimal type uses ".", but some locales use ","
  • users may type additional extra digits by mistake (hence "2500,12345" in my example code)
  • not all currencies have 2 fraction digits, some have 0 or 3

But the question is:

How to simplify this code?

As you can see in code, I am moving back and forth between String and NSDecimal to solve the problems mentioned above. And I am thinking that there is probably an easier way. I was hoping I could tell Swift to cut unnecessary fractions during first conversion from String to Decimal. Using currencyFormatter.maximumFractionDigits somehow. But when I print the value of currencyFormatter.maximumFractionDigits it has the correct value (2 in case of USD) only after currencyFormatter.numberStyle = .currency

Any ideas? :)

import Foundation

let amountString = "2500,12345"
var amountDecimal: Decimal = 0
var finalDecimal: Decimal = 0
print("amountString (\(type(of: amountString))):", amountString)

let currencyFormatter = NumberFormatter()
// Set locale to Polish, where decimal separator is "," instead of "."
currencyFormatter.locale = Locale(identifier: "pl_PL")
currencyFormatter.numberStyle = .decimal

// Convert String to Decimal and change , to .
if let number = currencyFormatter.number(from: amountString) {
    amountDecimal = number.decimalValue
    print("amountDecimal (\(type(of: amountDecimal))):", amountDecimal)
}

// set currency to USD
currencyFormatter.numberStyle = .currency
currencyFormatter.currencyCode = "USD"

let amountCurrency = currencyFormatter.string(for: amountDecimal)
print("amountCurrency (\(type(of: amountCurrency))):", amountCurrency ?? "0")

if let number = currencyFormatter.number(from: amountCurrency ?? "0") {
    finalDecimal = number.decimalValue
    print("finalDecimal (\(type(of: finalDecimal))):", finalDecimal)
}

EDIT: So far this is for iOS app where users will provide Strings using TextField with keyboard set to numerical with decimals.

mallow
  • 2,368
  • 2
  • 22
  • 63
  • The Locale is only relevant for the String value not the Decimal value, so what exactly is the problem you are trying to solve here? Why do you need the USD value as a string? – Joakim Danielson May 03 '21 at 14:52
  • I set Locale to Polish, because in Poland we use "," as decimal separator. Now let's say I have a textfield in iOS app. I will show numpad with decimals as a keyboard. Someone could write 2500,12345 (please notice extra digits - mistake). I need to change "," to ".". I do it by changing String to Decimal. But Decimal is has still too many digits, so I change it back to String now. With currencyCode it cuts extra digits. And later back to Decimal to store the value in CoreData. Too many steps IMHO and I would like to simplify this code, but I don't know how. – mallow May 03 '21 at 14:59
  • @mallow Where is your string coming from? It would be easier to restrict the input to two decimals or less depending on user's locale. – Leo Dabus May 03 '21 at 15:00
  • String will come from the TextField. Users will input them. I will use numpad keybord with decimals, but some locales use "," and that's why I need to change it to ".". I do it converting String to Decimal. – mallow May 03 '21 at 15:02
  • But why do you need to support different locales for the same user? Convert from string to Decimal using .current locale and then back to string with the same locale. What am I missing? – Joakim Danielson May 03 '21 at 15:06
  • 1
    @mallow this would work for any locale regardless of the number of decimals and/or the decimal separator https://stackoverflow.com/questions/29782982/how-to-input-currency-format-on-a-text-field-from-right-to-left-using-swift/29783546#29783546 – Leo Dabus May 03 '21 at 15:11
  • @JoakimDanielson What I need is to support many currencies. I know this doesn't need changing Locale. I've set Locale to "pl_PL" just to show an example where I have String with comma (some Locale uses "," instead of "." on numerical keyboards and I would get such String). Probably most folks here are from US, where decimal keyboard uses ".". But you know... maybe I am missing something :) – mallow May 03 '21 at 15:12
  • Ok I think I understand better but if the user is polish for instance then the natural decimal point for the user is "," so in my opinion if the amount for this case is in USD then what should be displayed is 2500.12 in the users locale so "2500,12 $" (or "2500,12 USD" or only "2500,12"). – Joakim Danielson May 03 '21 at 15:22
  • Displaying is one thing, but I what at the last step is Decimal with . and proper number of digits (0,2 or 3, depending on the currency) which I will store in Core Dat. I agree it looks like too many steps to do it. This is why I want to simplify the code. I’ve tried many options and failed. – mallow May 03 '21 at 15:26
  • @JoakimDanielson displaying without currency symbols is fine with me. What I need is to have proper number of digits per each currency. And change String to Decimal to store it in Core Data – mallow May 03 '21 at 15:28
  • @LeoDabus Thank you for this link. Interesting. But I need to investigate more as I am using SwiftUI. But if this works it could be good solution and cover physical keyboards. – mallow May 03 '21 at 15:31
  • 1
    @mallow https://stackoverflow.com/a/65783711/2303865 – Leo Dabus May 03 '21 at 15:57
  • @LeoDabus Thank you very much for this link. I will probably need few days to understand it and adapt to my case :) – mallow May 04 '21 at 04:58

0 Answers0