Second decimal place gets truncated if it is 0

378 Views Asked by At

I'm new to Swift, so bear with me. :)

I'm having trouble showing the output currency to two decimal places. Currently, it only shows one decimal place. For example, if I input $1.10, the output is $1.1.

However, if I input $1.11, the output is still $1.11.

func currencyInputDoubling() -> String {
    
    var number: NSNumber!
    let formatter = NumberFormatter()
    formatter.numberStyle = .currencyAccounting
    formatter.currencySymbol = CurrencyManager.shared.currentCurrency.sign
    formatter.maximumFractionDigits = 2
    formatter.minimumFractionDigits = 2
    
    var amountWithPrefix = self
    
    // remove from String: "$", ".", ","
    let regex = try! NSRegularExpression(pattern: "[^0-9]", options: .caseInsensitive)
    amountWithPrefix = regex.stringByReplacingMatches(in: amountWithPrefix, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSMakeRange(0, self.count), withTemplate: "")
    
    let double = (amountWithPrefix as NSString).doubleValue
    
    number = NSNumber(value: (double / 100))
    // if first number is 0 or all numbers were deleted
    guard number != 0 as NSNumber else {
        return ""
    }
    
    return "\(double / 100)"
}
2

There are 2 best solutions below

0
Rob On

I would suggest, at a minimum, using the formatter you created, rather than doing string interpolation. When you return "\(double / 100)", using simple string interpolation, that can’t avail itself of the formatter’s fractional digits setting.

Perhaps:

func currencyInputDoubling() -> String {    
    let formatter = NumberFormatter()
    formatter.numberStyle = .currencyAccounting
    formatter.currencySymbol = CurrencyManager.shared.currentCurrency.sign
    formatter.maximumFractionDigits = 2
    formatter.minimumFractionDigits = 2
    
    // remove from String: "$", ".", ","
    let digitsOnly = filter("0123456789".contains)` // or, if you want to use regex, simply `let digitsOnly = replacingOccurrences(of: "[^0-9]", with: "", options: .regularExpression)`

    // return formatted string
    guard let value = Double(digitsOnly) {
        return ""
    }

    return formatter.string(for: value / 100) ?? ""
}
0
Joakim Danielson On

I would suggest using Locale instead of currencySymbol and to create static number formatters that can be reused.

let currencyFormatter: NumberFormatter = {
    let formatter = NumberFormatter()
    formatter.numberStyle = .currency
    formatter.locale = .current

    return formatter
}()

let numberFormatter: NumberFormatter = {
    let formatter = NumberFormatter()
    formatter.numberStyle = .decimal
    formatter.locale = .current
    formatter.minimumFractionDigits = 2
    formatter.maximumFractionDigits = 2

    return formatter
}()

And then the method can be simplified as

func currencyInputDoubling(_ amountWithPrefix: String) -> String {
    guard let value = currencyFormatter.number(from: amountWithPrefix) else { return "" }

    return numberFormatter.string(from: value) ?? ""
}

If you need to set Locale to something else than .current you could pass it as an argument

func currencyInputDoubling(_ amountWithPrefix: String, using locale: Locale) -> String {
    currencyFormatter.locale = locale
    numberFormatter.locale = locale
    guard let value = currencyFormatter.number(from: amountWithPrefix) else { return "" }

    return numberFormatter.string(from: value) ?? ""
}