6
votes

I want to search the iPhone address book for a specific phone number, and then retrieve the contact name. I am currently looping through all contacts and extracting the multivalue properties and comparing against the value. This is taking way too much time. I have read the Apple addressbook guide, and they say:

"accomplish other kinds of searches, use the function ABAddressBookCopyArrayOfAllPeople and then filter the results using the NSArray method filteredArrayUsingPredicate:."

Can anyone give me an example on how to exactly do that?

Thanks.

4
it would be help. this is my answer about other question, stackoverflow.com/questions/4272164/…ChangUZ
Here's the most efficient way to do it using NSPredicate: hesh.am/2012/10/…Hesham

4 Answers

9
votes

If you want to do a search in the contacts with phone number, then I will tell you one suggestion.

1.Get all contacts

NSArray *thePeoples = (NSArray*)ABAddressBookCopyArrayOfAllPeople(addressBook);

2.Create another array(records) from the contacts array(thePeoples),

records:[ record1, record2, ....recordN ]

record: {name:"myContactName", phoneNumber:"1234567890"}

3.Search the mutableArray(records) with predicate.

NSPredicate * myPredicate = [NSPredicate predicateWithFormat:@"record.phoneNumber contains %@",string];

NSArray * filteredArray = [records filteredArrayUsingPredicate:myPredicate];

This is just a simple example to your solution, and remember phoneNumber is a multiValue field. So we will include an array as phone number in the model class variable.

3
votes

The following method will return an array that contains all of the contacts that have the given phone number. This method took 0.02 seconds to search 250 contacts on my iPhone 5 running iOS7.

#import <AddressBook/AddressBook.h>


-(NSArray *)contactsContainingPhoneNumber:(NSString *)phoneNumber {
    /*

     Returns an array of contacts that contain the phone number

     */

    // Remove non numeric characters from the phone number
    phoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""];

    // Create a new address book object with data from the Address Book database
    CFErrorRef error = nil;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
    if (!addressBook) {
        return [NSArray array];
    } else if (error) {
        CFRelease(addressBook);
        return [NSArray array];
    }

    // Requests access to address book data from the user
    ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {});

    // Build a predicate that searches for contacts that contain the phone number
    NSPredicate *predicate = [NSPredicate predicateWithBlock: ^(id record, NSDictionary *bindings) {
        ABMultiValueRef phoneNumbers = ABRecordCopyValue( (__bridge ABRecordRef)record, kABPersonPhoneProperty);
        BOOL result = NO;
        for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
            NSString *contactPhoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
            contactPhoneNumber = [[contactPhoneNumber componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:@""];
            if ([contactPhoneNumber rangeOfString:phoneNumber].location != NSNotFound) {
                result = YES;
                break;
            }
        }
        CFRelease(phoneNumbers);
        return result;
    }];

    // Search the users contacts for contacts that contain the phone number
    NSArray *allPeople = (NSArray *)CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
    NSArray *filteredContacts = [allPeople filteredArrayUsingPredicate:predicate];
    CFRelease(addressBook);

    return filteredContacts;
}
2
votes

Use This. this is my code. Make Array for searching.

        NSLog(@"=====Make People Array with Numbers. Start.");
        peopleWithNumber = [[NSMutableDictionary alloc] init];
        for (int i=0; i < [people count]; i++) {
            NSInteger phoneCount = [self phoneCountAtIndex:i];
            if (phoneCount != 0) {
                NSMutableArray *phoneNumbers = [[NSMutableArray alloc] init];
                for (int j=0 ; j < phoneCount ; j++) {
                    [phoneNumbers addObject:[self phoneNumberAtIndex:i phoneIndex:j]];
                }
                [peopleWithNumber addEntriesFromDictionary:
                 [NSDictionary dictionaryWithObjectsAndKeys:
                  [NSArray arrayWithArray:phoneNumbers],    [self fullNameAtIndex:i], nil]];
            }
        }
        NSLog(@"=====Make People Array with Numbers. End.\n");

Searching method. it(peopleWithNumber) would be faster than using array(people)

"NSArray *people = (NSArray *) ABAddressBookCopyArrayOfAllPeople(addressBook);"

- (NSArray *)searchNamesByNumber:(NSString *)number {

    NSString *predicateString = [NSString stringWithFormat:@"%@[SELF] contains '%@'",@"%@",number];
    NSPredicate *searchPredicate = [NSPredicate predicateWithFormat:predicateString,peopleWithNumber,number];
    NSArray *names = [[peopleWithNumber allKeys] filteredArrayUsingPredicate:searchPredicate];

    return names;
}
2
votes

You cannot use a predicateWithFormat with an array of ABRecordRef opaque types. But you can use predicateWithBlock:

[NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
    ABRecordRef person=(__bridge ABRecordRef)evaluatedObject;
    CFTypeRef theProperty = ABRecordCopyValue(person, kABPersonPhoneProperty);
    NSArray *phones = (__bridge_transfer NSArray *) ABMultiValueCopyArrayOfAllValues(theProperty);
    CFRelease(theProperty);
    BOOL result=NO;
    for (NSString *value in phones) {
        if ([value rangeOfString:@"3"].location!=NSNotFound) {
            result=YES;
            break;
        }
    }
    return result;
}];