Lookup a Contact Name Using Phone Number in ABAddressBook

I needed the ability to lookup the iOS address book using a phone number, and get the contact name associated with that number. I came up with the solution below which does the job but I haven’t tested its performance on address books with a large number of contacts.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
+ (NSString *)findNameForContactWithPhoneNumber:(NSString *)phoneNumber {
    ABAddressBookRef addressBook = ABAddressBookCreate();
 
    // Get all contacts in the addressbook
	NSArray *allPeople = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
 
	for (id person in allPeople) {
        // Get all phone numbers of a contact
        ABMultiValueRef phoneNumbers = ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonPhoneProperty);
 
        // If the contact has multiple phone numbers, iterate on each of them
        for (int i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
            NSString *phone = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, i);
 
            // Remove all formatting symbols that might be in both phone number being compared
            NSCharacterSet *toExclude = [NSCharacterSet characterSetWithCharactersInString:@"/.()- "];
            phone = [[phone componentsSeparatedByCharactersInSet:toExclude] componentsJoinedByString: @""];
            phoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:toExclude] componentsJoinedByString: @""];
 
            if ([phone isEqualToString:phoneNumber]) {
                NSString *firstName = (__bridge_transfer NSString*)ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonFirstNameProperty);
                NSString *lastName = (__bridge_transfer NSString*)ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonLastNameProperty);
 
                return [NSString stringWithFormat:@"%@ %@", firstName, lastName];
            }
        }
    }
 
    return nil;
}

Please leave a comment if you come up with a more efficient way to do it.

Update

The method above proved to be ambitious but rubbish. Its performance is really bad with large address books specially when you are looking up multiple phone numbers. I’ve written a much more efficient way using NSPredicate to do the same thing but much faster. I’m also using Grand Central Dispatch to copy the contacts from the address book only once instead of doing it every time the method is called.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
+ (NSString *)findNameForContactWithPhoneNumber:(NSString *)phoneNumber {
    static NSMutableArray *contacts = nil;
    static dispatch_once_t onceToken;
 
    dispatch_once(&amp;onceToken, ^{
        contacts = [[NSMutableArray alloc] init];
        ABAddressBookRef addressBook = ABAddressBookCreate();
        // Get all contacts from the address book
        NSArray *allPeople = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBook);
 
        for (id person in allPeople) {
            Contact *contact = [[Contact alloc] init];
 
            NSString *firstName = (__bridge_transfer NSString*)ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonFirstNameProperty);
            NSString *lastName = (__bridge_transfer NSString*)ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonLastNameProperty);
 
            [contact setName:[NSString stringWithFormat:@"%@ %@", firstName, lastName]];
            NSMutableArray *tempArray = [[NSMutableArray alloc] init];
 
            // Get all phone numbers of a contact
            ABMultiValueRef phoneNumbers = ABRecordCopyValue((__bridge ABRecordRef)(person), kABPersonPhoneProperty);
 
            // If the contact has multiple phone numbers, iterate on each of them
            NSInteger phoneNumberCount = ABMultiValueGetCount(phoneNumbers);
            for (int i = 0; i < phoneNumberCount; i++) {
                NSString *phoneNumberFromAB = [(__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, i) unformattedPhoneNumber];
                [tempArray addObject:phoneNumberFromAB];
            }
            [contact setNumbers:tempArray];
            [contacts addObject:contact];
        }
        CFRelease(addressBook);
    });
 
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"numbers contains %@", phoneNumber];
    NSArray *filteredArray = [contacts filteredArrayUsingPredicate:predicate];
 
    return [(Contact *)[filteredArray lastObject] name];
}

I copy the contacts from the address book into an array of Contact objects which look like this:

1
2
3
4
5
6
@interface Contact : NSObject
 
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSArray *numbers;
 
@end

And I use a category on NSString to get an unformatted version of the phone number:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface NSString (PhoneAdditions)
 
- (NSString *)unformattedPhoneNumber;
 
@end
 
@implementation NSString (PhoneAdditions)
 
- (NSString *)unformattedPhoneNumber {
    NSCharacterSet *toExclude = [NSCharacterSet characterSetWithCharactersInString:@"/.()- "];
    return [[self componentsSeparatedByCharactersInSet:toExclude] componentsJoinedByString: @""];
}
 
@end

Update 2

I’ve updated the code to support new authorisation in iOS 6, migrated it to ARC and added a method to get the photo of the contact. I’ve also moved the code to a GitHub Gist for easier maintenance.

Cycling for Charity – 300 km Across Egypt

From the 28th to the 31st of December I will be cycling from Taba to Luxor as part of the Global Biking Initiative (GBI) which aims to gather bikers from all around the world to bike in support for charity.

What is GBI?

GBI started in 2007 with just 2 Vodafone employees from Germany that biked all the way to the UK. Each year the tour got bigger and bigger. By 2012, 365 bikers from around the world (including 47 from Egypt) biked from Oslo to Dusseldorf and raised 210,000 Euros for various charity causes. This year there are going to be 2 tours; the usual one in Europe and a local one in Egypt.

Charity Causes

Charity funds are raised before the tour starts. Each country raises money for a charity cause they choose. From 2009 till 2013, Egypt has raised a total of 775,000 EGP. Starting from 2011, the money raised goes to Alashanek Ya Balady NGO, with the goal of supporting 1,000 poor families. In 2013, the Egyptian team continues to work on the same cause.

The Route for GBI 2012

GBI Egypt starts at Taba and goes through Dahab, Sharm El Sheikh, Hurghada, Marsa Alam, Qena and ends in Luxor totalling 300 km over 4 days.

How Can I Help?

GBI is all about raising funds! Support us by donating to our cause. 100% of the raised funds will be given to Alashanek Ya Balady as all the operational costs of the GBI are covered by our sponsors. Help us make this world a better place – especially for the non-privileged of us!

To donate please transfer the desired amount to HSBC account number 019-107689-001 with the name Hesham Abd-Elmegid Mostafa and drop me and email at hesham.abdelmegid@gmail.com for confirmation.

More Info

Learn more about GBI at the official website: http://gbi-ev.org

Also below is a feature story covering one of the recent GBI trainings.

6 iPhone apps to help you stay fit

Over the past year I have been growing an addiction to staying fit and living a healthy lifestyle. During that year I have realised that staying fit does not require expensive gym memberships or personal trainers. All you need is your will, your iPhone and some of the awesome apps below.

RunKeeper – Free

To me RunKeeper is the best app to keep track of your outdoor activities. It tracks your walking/running/cycling activities using GPS and displays useful information like the distance covered, average speed, elevation climb and heart rate (if you have a Polar heart rate monitor). RunKeeper will also let you set goals and keep track of your progress towards these goals, which is always a great motivation to go on.

Hundred PushUps – $1.99

Anyone who is serious about exercising doesn’t only want to do cardiovascular activities, but also increase their strength and muscle mass. Pushups are a great way to achieve that. Hundred PushUps is both a challenge and a training program to be able to do 100 consecutive pushups in 6 weeks. The app gives a step by step guide by measuring your initial performance, giving you weekly targets and measuring you progress against those targets.

GymPact – Free

If you have a gym membership but you are failing to go to the gym regularly or if you are generally skipping lots of your planned exercises, GymPact is made specifically for you. GymPact lets you earn cash rewards for exercising. Make a Pact of how many days you’ll work out per week, set the stakes of how much you’ll pay if you don’t, and check-in via GPS at the gym. It also has RunKeeper integration that lets you count runs, bike rides or walks.

Charity Miles – Free

Charity Miles is similar to GymPact in the sense that it rewards you with money for your workouts, but instead of you earning that money, it goes to a charity cause you select. It’s a great motivator knowing that the kilometers you make while walking, running or cycling will not only be of a great impact on yourself but are also a tiny step into making this world a better place for everyone.

All-in Yoga – $0.99

In between cardio and strength training days you need to be stretching and relaxing your body, and Yoga is the best way to do so. All-in YOGA has over 300 poses with photos, videos and audio guides. It also features 3D muscle models for all the poses it has. All-in Yoga also features over 40 Yoga programs that you could follow, and you can even create your own program.

WiScale – Free

Tracking your progress is as important as the training itself. Nothing is more motivating than seeing your weight go down, your body fat percentage decrease or your muscle mass increase. WiScale allows you to easily monitor essential health and fitness data like weight, fat mass, lean mass, BMI, blood pressure and heart rate. WiScale works with the Withings Wi-Fi Body Scale, a smart scale that measures your weight, fat percentage along with other stuff and automatically sends them to the app (it can even tweet on your behalf if you want!).

Stay healthy, stay fit, enjoy life.

I like being weird

If…

  • Bringing your homemade food to work and refusing to eat fastfood with everyone else makes you weird
  • Waking up 4 hours before everyone else does and sleeping before some people have even finished their workday makes you weird
  • Refusing to eat dessert or extra servings at parties makes you weird
  • Refusing to fit with the majority and refusing to do things like everybody else makes you weird
  • Preferring to be sweaty and exhausted to being comfy and lazy makes you weird

Then I TOTALLY like being weird.

Inspired by NerdFitness.