A Very Simple NSNumberFormatter Currency Subclass

Content

A Very Simple NSNumberFormatter Currency Subclass

Posted in:

NSNumberFormatter is the class of choice when formatting numeric input for text fields. If you choose to use a currency symbol prefix then the default behaviours can be less than ideal.

If the user inputs the currency symbol then all is well. If not (inputting say 1.50 instead of £1.50) then the formatter raises an error and the user is nagged that a formatting error has occurred.

A simple fix is to subclass NSNumberFormatter and add the missing currency symbol if it is missing. The modified string can then be forwarded to the base class.

The users locale can have a profound affect on the way that an instance of NSNumberFormatter is configured. This example code should be able to handle the following formats:

£1.00, -£1.00, 1.00 €, -1.00 €.

In other words, the method should be capable of accepting positive and negative input of currency values where:

  • The currency symbol is either present or absent.
  • The number is positive or negative.
  • The curency symbol is either prefixed or suffixed.

The subclass need consist of a single method:
- (BOOL)getObjectValue:(id *)obj forString:(NSString *)string errorDescription:(NSString **)error

/*
 
 get object value for string
 
 The default formatter behaviour will generate an error if the currency symbol is omitted
 from the input string.
 To make life easy for the user we will prefix/suffix the currency symbol if it is missing.
 
 Note that some locales suffix the currency.
 
 */
- (BOOL)getObjectValue:(id *)obj forString:(NSString *)string errorDescription:(NSString  **)error
{
    NSString *positivePrefix = [self positivePrefix];
    NSString *positiveSuffix = [self positiveSuffix];
    NSString *negativePrefix = [self negativePrefix];
    NSString *negativeSuffix = [self negativeSuffix];
    NSString *minusSign = [self minusSign];
    NSString *prefix = nil, *suffix = nil;
    
    string = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
    NSScanner *scanner = [NSScanner scannerWithString:string];
    BOOL isPositive = YES;
    
    // is number negative
    if ([minusSign length] > 0) {
        if ([scanner scanString:minusSign intoString:NULL]) {
            isPositive = NO;
        }
    }
    [scanner setScanLocation:0];    // reset scan location
    
    // get prefix and suffix
    if (isPositive) {
        prefix = positivePrefix;
        suffix = positiveSuffix;
    } else {
        prefix = negativePrefix;
        suffix = negativeSuffix;
    }
    
    // if prefix defined
    if ([prefix length] > 0) {
        if (![scanner scanString:prefix intoString:NULL]) {
            
            // prefix not found
            // if number is negative it may have a multicharacter prefix such as -£,
            // in which case check that minus sign alone is not present
            if (!isPositive && [scanner scanString:minusSign intoString:NULL]) {
                string = [string substringFromIndex:[scanner scanLocation]];
            }
            
            // add prefix
            string = [NSString stringWithFormat:@"%@%@", prefix, string];
        }
    }
    
    // if suffix defined
    if ([suffix length] > 0) {
        NSString *s = @"";
        if ([string length] >= [suffix length]) {
            NSUInteger index = [string length] - [suffix length];
            s = [string substringFromIndex:index];
        }
        if (![s isEqualToString:suffix]) {
            /* We can simply add suffix here even if it is a multicharacter sequence (say spc-€).
               If input is say 1€ our result is 1€ € but this parses okay.
               1€ is deemed valid input and the remainder of the string is discarded, whatever it is.
             */
            string = [NSString stringWithFormat:@"%@%@", string, suffix];
        }
            
    }
    
    return [super getObjectValue:obj forString:string errorDescription:error];
}

Note that a useful web utility for formatting code samples such as this can be found here http://formatmysourcecode.blogspot.com/. Another good resource is http://qbnz.com/highlighter/ which supports syntax highlighting for a wide range of languages