Search code examples
objective-cnsnumberformatter

NSNumberFormatter: dispatch_once where Currency changes


I have a tableview where a list of NSDecimalNumber will be displayed with their own currencies.

I use a dispatch_once on a NSNumberFormatter but notice that sometimes it'll totally ignore my commands to set the fraction digits or suffix on fraction digits altogether.

Currently the only way I've been able to resolve this is to init the NSNumberFormatter every row which is a bit silly.

ie:

- (NSNumberFormatter *)currencyFormatWithCurrency:(NSString *)currency
{
    static NSNumberFormatter *_formatter = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _formatter = [[NSNumberFormatter alloc] init];
    });

    [_formatter setCurrencyCode:currency];
    [_formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    [_formatter setMaximumFractionDigits:0];
    [_formatter setGeneratesDecimalNumbers:NO];
    [_formatter setRoundingMode:NSNumberFormatterRoundUp];

    return _formatter;
}

// someCurrency is a string that will change per row
// someAmount is a NSDecimalNumber that will change per row
  NSNumberFormatter *formatter = [self currencyFormatWithCurrency:someCurrency];
    NSString *formattedAmount = [formatter stringFromNumber:someAmount];

    self.amountLabel.text = formattedAmount;

Sometimes with the above if I feed in a figure like 80000, it'll return 80,000.00 (sometimes it depends on the currency).

I guess I could just init the NSNumberFormatter and nil it on each row.

Perhaps I'm doing it wrong, but if there is perhaps a way to ensure that I don't need to keep creating the NSNumberFormatter to ensure it'll understand the rules I give it?

Many thanks


Solution

  • NSNumberFormatter is not thread safe. That will lead to all kinds of problems. (Well, the way you use it cannot really be thread safe, because you might be modifying it in one thread while another thread is using it, but just using an NSNumberFormatter from two threads is not safe).

    You can always do something in a method like

    NSString* lastCurrency = nil;
    NSNumberFormatter* formatter = nil; 
    
    for (;;) {
        ...
        NSString* currency = ...;
        if (! [lastCurrency isEqualToString:currency]) {
            lastCurrency = currency; 
            formatter = ...;
        }
    }
    

    Works well if in most cases the currency is unchanged.