1

I have a question regarding the predicate I am using in the following code

   NSMutableArray *records = (__bridge NSMutableArray *)ABAddressBookCopyArrayOfAllPeople( addressBook );

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


    @try {
            [records filterUsingPredicate:predicate];
    }
    @catch (NSException *exception) {
        NSLog(@"%@",exception);
    }
    @finally {
        //
    }

The exception I get is:

[<__NSCFType 0x6e2c5e0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key record.

I've been trying to find a guide on predicates for address book with no luck though. Any suggestions?

John Lane
  • 1,112
  • 1
  • 14
  • 32
  • Do the objects you are filtering have a property called `record` and does that object (record) have a property called `phoneNumber`? If not, the same exception will be thrown. Regarding your request for a guide, I have found [this book](http://www.amazon.com/Core-Data-iOS-Data-Driven-Applications/dp/0321670426) to be pretty good. It is simple and succinct. – lindon fox Nov 29 '12 at 10:36
  • They appear to be of __NSCFType type – John Lane Nov 29 '12 at 13:19

1 Answers1

1

You can't filter the address book using NSPredicates. Additionally, phoneNumber is not a field of ABRecordRef. Users can have multiple phone numbers, so you need to inspect each one.

You would do something like this:

CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(addressBook);

NSMutableArray *matchingPeople = [NSMutableArray array];
for (CFIndex i = 0; i < CFArrayGetCount(people); i++) {
    ABRecordRef person = CFArrayGetValueAtIndex(people, i);
    ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
    int phoneNumbers = ABMultiValueGetCount(phones);
    if (phoneNumbers > 0) {
        for (CFIndex i = 0; i < phoneNumbers; i++) {
            NSString *phone = (NSString *)CFBridgingRelease(ABMultiValueCopyValueAtIndex(phones, i));
            if ([phone rangeOfString:@"123"].location != NSNotFound) {
                [matchingPeople addObject:person];
                break;
            }
        }
    }
    CFRelease(phones);
}
CFRelease(people);

Personally, I wouldn't add ABRecordRefs to the array--I'd create a value object that includes just the fields you want from the record and add that, so when you're done with the loop, you can ensure you don't have any dangling CFTypes.

Christopher Pickslay
  • 17,523
  • 6
  • 79
  • 92
  • 3
    To accomplish other kinds of searches, use the function ABAddressBookCopyArrayOfAllPeople and then filter the results using the NSArray method filteredArrayUsingPredicate:. From Apple's dev docs: http://developer.apple.com/library/ios/#documentation/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Chapters/DirectInteraction.html#//apple_ref/doc/uid/TP40007744-CH6-SW3 – John Lane Nov 29 '12 at 23:31