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.

  • Jason Larynx

    Why don’t you release address book with CFRelease?

  • http://twitter.com/HeshamMegid Hesham Abd-Elmegid

    You are right, I was missing a CFRelease there.

    I’ve just added it. Thanks :)

  • jpaquete

    ABMultiValueGetCount(phoneNumbers) should be in a var, Otherwise megapower! Thx

  • http://twitter.com/HeshamMegid Hesham Abd-Elmegid

    Fixed! :)

  • ambuj shukla

    Sir I’m making a birthday reminder app in which I have a need to store first name list in to array so that they get store in array[0] array[1] and so on that first name of contacts in array[0] second first name contacts in array[1] and so on.

  • http://twitter.com/HeshamMegid Hesham Abd-Elmegid

    The above code should work for you if you remove the part that concatenates the first name and last name and use the contacts array directly instead of using the filteredContacts array.

  • marcinx

    Best ABAddressBook reference so far. Well done!

  • Pingback: search specific number in iphone contacts | BlogoSfera()

  • Yash Changediya

    Sir,How do I check for the contact type into the Address Book. If suppose the Facebook contacts are synced so while retriving them how to check whether it is a local contact or one from Facebook??

  • Subbu Sama

    Hi,
    How can i get particular total record information from phone number. I’m using the first one, I’m able to get the contact name from the phone number. Actually I’m working on calling app, when i entered any number in the keyboard after dialing the number i should get the particulars of the number what i’ve entered.
    Thanks in advance!!