swiftswiftuitextfieldnsnumberformatter

Formatting currency in Textfield while typing


I need to format the currency while typing in TextField. I have tried many answers but nothing worked in my case. My currency is INR. When the user types it should change from, for example "1200" to "₹1,200". I am able to convert it to "₹1200", but the comma is missing.

My implementation:

extension NumberFormatter {
    static var currencyFormatter: NumberFormatter {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.currencySymbol = "₹"
        formatter.maximumFractionDigits = 0
        formatter.usesGroupingSeparator = true
        formatter.currencyCode = "INR"
        formatter.currencyGroupingSeparator = ","
        formatter.groupingSeparator = ","
        formatter.maximumIntegerDigits = 10
        formatter.locale = Locale(identifier: "en_IN")
        return formatter
    }
}

@State var testAmount = String()

TextField("₹ 0", text: $testAmount)
    .onChange(of: testAmount){newvalue in
        if let parsedAmount = Double(newvalue){
            self.testAmount = NumberFormatter.currencyFormatter.string(from: NSNumber(value: parsedAmount)) ?? "" 
        }
    }
    .keyboardType(.numberPad)

Solution

  • Double(newvalue) will fail to parse the string once it has a in it.

    You can parse and format the number using the FormatStyle APIs. You can use a Decimal.FormatStyle.Currency.

    @State private var testAmount = ""
    
    let formatStyle = Decimal.FormatStyle.Currency
        .currency(code: "INR")
        .precision(.fractionLength(0))
        .locale(.init(identifier: "en_IN"))
    
    var body: some View {
        TextField("₹ 0", text: $testAmount)
            .onChange(of: testAmount){ _, newValue in
                if let parsedAmount = try? formatStyle.parseStrategy.parse(newValue) {
                    self.testAmount = formatStyle.format(parsedAmount)
                }
            }
    }
    

    In my opinion though, formatting while typing is rather confusing. Formatting after the user ends editing is a much better user experience. TextField has a dedicated initialiser for this:

    @State private var testAmount: Decimal = 0
    
    let formatStyle = Decimal.FormatStyle.Currency
        .currency(code: "INR")
        .precision(.fractionLength(0))
        .locale(.init(identifier: "en_IN"))
    
    var body: some View {
        TextField("₹ 0", value: $testAmount, format: formatStyle)
    }