0

How do I get a string into a 'currency' format USD(xxx)? I am trying to use the following library: https://github.com/peek-travel/swift-currency

I want to be able to take a string:

var testStr1 = "$30.01"
var testStr2 = "$ 30.01"

and convert this into a currency as I have read from several posts using a double or float is bad, but if I start with a String, what else can I convert it to?

I thought I could use the "import Currency" library to do this, but this is not working.

let updatedString = testStr1.replacingOccurrences(of: "$", with: "") 
                        let formatter = NumberFormatter()
                        formatter.locale = Locale.current // USA: Locale(identifier: "en_US")
                        formatter.numberStyle = .decimal
                        let number = formatter.number(from: test)
var dollars = USD(updatedString)

How do I get a string into a 'currency' format USD(xxx)? If there is a better way to accomplish this?

Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
Rolando
  • 58,640
  • 98
  • 266
  • 407

2 Answers2

2

The basic concept presented by Himanshu works fine, but your problem isn't necessarily making use of an appropriate formatter, but how to fix your input, as the formatter expects a NSNumber and not a String.

So a quick internet check had me looking at Remove all non-numeric characters from a string in swift

So I could take a String, filter out all the "non numerical" junk and then make a Double out of it.

let input = Double(value.filter("0123456789.".contains))

from there I was able to borrow the concept from Himanshu and make a simple format function

func format(_ value: String, locale: Locale = Locale.current) -> String? {
    
    guard let input = Double(value.filter("0123456789.".contains)) else { return nil }

    //value.trimmingCharacters(in: .whitespacesAndNewlines)
    
    let currencyFormatter = NumberFormatter()
    currencyFormatter.usesGroupingSeparator = true
    currencyFormatter.numberStyle = .currency
    
    currencyFormatter.locale = locale
    
    return currencyFormatter.string(from: NSNumber(value: input))
}

I then made use of a Playground to test it using

var testStr1 = "$30.01"
var testStr2 = "$ 30.01"

format(testStr1, locale: Locale(identifier: "en_US")) // $30.01
format(testStr2, locale: Locale(identifier: "en_US")) // $30.01

format(testStr1, locale: Locale(identifier: "fr_FR")) // 30,01 €
format(testStr2, locale: Locale(identifier: "fr_FR")) // 30,01 €

format(testStr1, locale: Locale(identifier: "de_DE")) // 30,01 €
format(testStr2, locale: Locale(identifier: "de_DE")) // 30,01 €

Now, if you specifically want to use USD(xxx) as the format, then you could simply use a basic NumberFormatter and generate your own String from the resulting conversion of the input to a Double

I have read from several posts using a double or float is bad

So, yes, maintaining a currency value as a Double or Float is generally a bad idea, currency values are typically maintained as a Int or Long, but this is due to how Double and Float representation works in computers, for the, general, presentation, you should be fine, but each use case needs be assessed.

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thank you for suggestion and explanation. – Himanshu Patel Jan 05 '21 at 06:28
  • @HimanshuPatel I thought you wanted the result as USD ... using the currency code. So what is the expected output `"USD $30.01"`? – Leo Dabus Jan 05 '21 at 06:29
  • 1
    @madprogrammer. Remember that Swift is a type inferred language `locale: Locale = .current` and there is no need to initialize a new `NSNumber`. you can use Formatter's method `string(for: Any)` and pass the double – Leo Dabus Jan 05 '21 at 06:34
  • 1
    Another thing that I would definetly fix in this code is that every time you call this method it will create a new Number Formatter – Leo Dabus Jan 05 '21 at 06:36
  • @LeoDabus I tried `string(for: Any)` and it gave me a compiler error - or I was using the wrong func :P - in any case - it works – MadProgrammer Jan 05 '21 at 09:31
  • @MadProgrammer I am not sure what you did wrong. I never use string from method. You can check all my posts All of them I use string(for: value) – Leo Dabus Jan 05 '21 at 13:50
  • @LeoDabus *"Another thing that I would definetly fix in this code is that every time you call this method it will create a new Number Formatter "* - Yes, I agree, but this is StackOverFlow, not "how you should code properly flow" :P - where we present ideas to help solve problems, after which developers are expected to then go an expand on and fit into their particular use case - just my point of view :P – MadProgrammer Jan 05 '21 at 21:56
1
let currencyFormatter = NumberFormatter()
currencyFormatter.usesGroupingSeparator = true
currencyFormatter.numberStyle = .currency
// localize to your grouping and decimal separator
currencyFormatter.locale = Locale.current

// We'll force unwrap with the !, if you've got defined data you may need more 
error checking
let priceString = currencyFormatter.string(from: 9999.99)!
print(priceString) // Displays $9,999.99 in the US locale

**Forcing a Custom Locale**
You can override the users locale to display specific currency formats by changing the Locale using the identifier.

currencyFormatter.locale = Locale(identifier: "fr_FR")
if let priceString = currencyFormatter.string(from: 9999.99) {
    print(priceString) // Displays 9 999,99 € in the French locale
}

currencyFormatter.locale = Locale(identifier: "de_DE")
if let priceString = currencyFormatter.string(from: 9999.99) {
    print(priceString) // Displays 9.999,99 € in the German locale
}
Himanshu Patel
  • 1,015
  • 8
  • 26
  • This doesn't appear to start with the test strings I have in my OP that begin with strings that I want to convert to USD? – Rolando Jan 05 '21 at 05:55