7

I have a UITextField with a mask formatting my input to the BRL currency, so input 1700 from keyboard results in R$ 1.700,00 text, but since I need this value as double, NumberFormatter was used like this:

var currencyFormatter: NumberFormatter {
    let formatter = NumberFormatter()
    formatter.locale = Locale(identifier: "pt_BR")
    formatter.numberStyle = .currency
    formatter.maximumFractionDigits = 2
    return formatter
}

But when I tried to convert to NSNumber it gives me nil.

let input = "R$ 1.700,00"
currencyFormatter.number(from: input) // prints nil

Oddly, the converter works when a number is provided, and the string result can be converted to a Number.

let string = currencyFormatter.string(from: NSNumber(1700.00)) // prints "R$ 1.700,00"
currencyFormatter.number(from: string) // prints 1700.00

I started to investigate and find out what the difference is between my input and the resulting currencyFormatter string from the code below.

let difference = zip(input, string).filter{ $0 != $1 } // Prints [(" ", " ")]

It may look the same but currencyFormatter generates a string with a non-breaking space after the currency symbol while my input has a normal space. Replacing my input space with a non-breaking space makes the formatter work properly.

let nonBreakingSpace = "\u{00a0}"
let whitespace = " "
var convertedInput = "R$ 1.700,00".replacingOccurrences(of: whitespace, with: nonBreakingSpace) // prints R$ 1.700,00 or R$\u{00a0}1.700,00

currencyFormatter.number(from: convertedInput) // Now it works and prints 1700.00

Finally my question is, why does NumberFormatter with a currency numberStyle only work with a non-breaking space in this scenario?

Boann
  • 48,794
  • 16
  • 117
  • 146
Cayoda
  • 189
  • 6
  • 3
    I cannot tell you why, but adding `formatter.isLenient = true` makes the conversion work in my test. – Martin R May 22 '21 at 19:08
  • 1
    I can't find any documentation on why it inserts any kind of whitespace between the currency symbol and the numeric value. – Duncan C May 22 '21 at 19:16
  • 2
    Inserting a non-breaking space in the number-to-string conversion makes some sense, as it prevents the resulting string from being split across lines in multi-line text. *Expecting* a non-breaking space in the string-to-number conversion makes no sense to me. I have no idea if that is documented and/or to be expected. – Martin R May 22 '21 at 19:20
  • 1
    @MartinR Interesting, I did the test in currencies which the currency symbol comes before the digits(like Bolivian Bolívar and Brazilian Real) and it worked, but I have no idea why either, isLenient property documentation seems pretty vague about it. – Cayoda May 22 '21 at 22:40
  • 1
    My guess is that without lenient mode, NumberFormatters require that the output of `string(from:)` and the input to `number(from:)` have to match exactly. As Martin says, using a non-breaking space makes sense so the whole currency string doesn't get split between lines. With `isLenient = true`, the formatter is willing to overlook the difference between breaking and non-breaking space. – Duncan C May 23 '21 at 00:57
  • https://developer.apple.com/forums/thread/124911 – matt May 23 '21 at 02:26
  • `!=` is already a function so you don’t need to make a new closure out of it. –  May 23 '21 at 02:36
  • @Jessy posted to wrong question? – matt May 23 '21 at 06:02
  • 1
    @matt no he was referring to `.filter { $0 != $1 }` that can be written as `.filter(!=)` which I hadn't thought of myself so that was a cool tip. Thanks Jessy. – Joakim Danielson May 23 '21 at 06:45
  • @MartinR you should post your `isLenient = true` trick as an answer, along with the link Matt posted, where Quinn (The Eskimo) talks about number formatters being focused on display, and subject to change over time. – Duncan C May 23 '21 at 11:52

0 Answers0