From time to time you may need to display some information which is locale specific and we may not be able to do it using NSNumberFormatter and NSDateFormatter. Any example might be displaying telephone numbers in a certain format. In such a case, we can create a custom formatter, which is what this tutorial does.

Introduction
The goal of this article is to create a custom formatter, used to display locale specific data. In this tutorial, I will create a new class to format phone numbers based on the user’s current locale. If the locale is set to “en_US” then the phone number looks like 1(111)111-1111.

NSFormatter
To create a custom formatter, start by creating a new class which inherits from NSFormatter. In Xcode click File -> New File -> NSObject subclass, I have named my file “PhoneNumberFormatter”. The new class you create will inherit from NSObject, simple delete that and inherit from NSFormatter. This is how the header file looks like

#import <UIKit/UIKit.h>
 
@interface PhoneNumberFormatter : NSFormatter {
 
NSLocale *locale;
}
 
@property (nonatomic, copy) NSLocale *locale;
 
- (NSString *) stringFromPhoneNumber:(NSNumber *)aNumber;
 
@end

We have a locale property, to keep track of the current user’s locale and a method which takes a NSNumber and returns its string representation.

Since the class inherits from NSFormatter, it needs to override some methods. The three methods that we have to override are stringForObjectValue:(id)anObject, getObjectValue:(id)anObject forString:(NSString *)string errorDescription:(NSString *)error and attributedStringForObjectValue:(id)anObject withDefaultAttributes:(NSDictionary *)attributes. This has been my understanding so far, if I’ am wrong please send me an email. All these methods are really useful in Mac OS X development where we can assign a cell to an NSFormatter object (I have tried to do this in IB for iPhone and it does not work). stringForObjectValue returns NSString object that textually represents the cell’s value and the other two methods are also useful in Mac OS X development and not really helpful in iPhone development. As a result, I will not be talking a lot about these methods.

Lets see how the initWithLocale and stringFromPhoneNumber methods looks like

- (void) initWithLocale {
[super init];
 
locale = [NSLocale currentLocale];
}
 
- (NSString *) stringFromPhoneNumber:(NSNumber *)aNumber {
 
NSString *localeString = [locale localeIdentifier];
NSString *tempStr = [[NSString alloc] initWithString:@""];
NSRange range;
range.length = 3;
range.location = 3;
//Returns the phone number 2032225200 as 1(203)222-5200
if([localeString compare:@"en_US"] == NSOrderedSame) {
NSString *areaCode = [[aNumber stringValue] substringToIndex:3];
NSString *phone1 = [[aNumber stringValue] substringWithRange:range];
NSString *phone2 = [[aNumber stringValue] substringFromIndex:6];
 
tempStr = [NSString stringWithFormat:@"1(%@)%@-%@", areaCode, phone1, phone2];
}
 
return tempStr;
}

In the initWithLocale method, we get the current user’s locale and copy it to the internal variable. In stringFromPhoneNumber we write our logic to display the text representation of the NSNumber value, based on the locale. This is a very simple example and I’ am sure there is a better way to parse and display the information.

Let’s also take a look at stringForObjectValue method

- (NSString *) stringForObjectValue:(id)anObject {
 
if(![anObject isKindOfClass:[NSNumber class]])
return nil;
else
return [self stringFromPhoneNumber:anObject];
}

We first check if the object is kind of NSNumber and if it is we simple pass the control to stringFromPhoneNumber message.

This is how we will use the code in the application

- (void)applicationDidFinishLaunching:(UIApplication *)application {
 
NSNumber *phoneNumber = [NSNumber numberWithInt:1231231212];
PhoneNumberFormatter *pnf = [[PhoneNumberFormatter alloc] initWithLocale];
 
NSLog(@"Phone Number: %@ for locale: %@", [pnf stringFromPhoneNumber:phoneNumber], [[pnf locale] localeIdentifier]);
 
[pnf release];
 
// Override point for customization after application launch
[window makeKeyAndVisible];
}

The phone number and the locale will be displayed on the debugger window.

Conclusion
Creating a custom formatter, comes in handy when we have to display data like phone numbers and format it according to the user’s current locale. By doing this, we also keep our code clean.

Happy Programming,
iPhone SDK Articles


Attachments

Suggested Readings

 

7 Responses to Localizating iPhone Apps – Custom Formatter

  1. bob says:

    if([localeString compare:@"en_US"] == NSOrderedSame)
    should be written as
    if([localeString isEqualToString:@"en_US"])

  2. iPhone SDK Articles says:

    >@Anonymous To which class does this property “setMaximumIntegerDigits” to?

    I am not sure I understand your question.

    Happy Programming,
    iPhone SDK Articles

  3. iPhone SDK Articles says:

    >@mmilano You need to do this in text changed method of the text box and then count the number of characters entered.

    Hope this helps.

    Happy Programming,
    iPhone SDK Articles

  4. mmilano says:

    >a question about making a custom formatter.

    suppose i want to have my input formatted to the pattern, “##-##-##”. more importantly, i would like to have an input field that allows the person to enter their number; as they type, the value displayed should build to the right.

    example, step by step:
    person types: 1
    display: 1

    person types: 3
    display: 13

    person types: 5
    display: 13-5

    person types: 7
    display: 13-57

    person types: 8
    display: 13-57-8

    person types: 9
    display: 13-57-89

    any advice?
    i can only get the formatter to return the following – in step three (above), the results are:

    display: “1-35″
    instead of “13-5.”

    and so on( e.g.: “1-35-78″ in step 5.

    thanks in advance.

  5. Anonymous says:

    >hi, and thanks.
    was wondering about the init of the custom formatter.

    if i want to set some of the specific properties, such as “setMaximumIntegerDigits”, as an example, would i want to do that in an override of the init method?

  6. iPhone SDK Articles says:

    >The source code is fixed. I had to change the method name from init to initWithLocale

    Happy Programming,
    iPhone SDK Articles

  7. Anonymous says:

    >when i compile your code with release mode,
    it has exception error while running.