In OS X (Mac 10.8.2) I have added a category to ABPerson
to return phone numbers as an array of Phone
objects. The Phone
object has two properties, label
and value
. I want to get all phone numbers and filter them to include only those whose label
matches an input.
If I use nested valueForKey:
calls on either the entire array of ABPerson
s or on an individual person with the keys @"phones"
and @"label"
, then I get the desired result. If, however, I concatenate the valueForKey:
s to a single valueForKeyPath:
then I still get the proper result when searching the entire array, but not when evaluating a single person object:
#import <Cocoa/Cocoa.h>
#import <AddressBook/AddressBook.h>
@interface Phone : NSObject
@property (retain) NSString *label;
@property (retain) NSString *value;
@end
@implementation Phone
- (void)dealloc {
self.label = nil;
self.value = nil;
[super dealloc];
}
@end
@interface ABPerson (phones)
- (NSArray *)phones;
@end
@implementation ABPerson (phones)
- (NSArray *)phones {
NSMutableArray *phones = [NSMutableArray array];
ABMultiValue *values = [self valueForProperty:kABPhoneProperty];
for (NSInteger i = 0; i < [values count]; i++) {
Phone *phone = [[Phone alloc] init];
phone.label = ABLocalizedPropertyOrLabel([values labelAtIndex:i]);
phone.value = [values valueAtIndex:i];
[phones addObject:phone];
[phone release];
}
return [NSArray arrayWithArray:phones];
}
@end
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSArray *people = [[ABAddressBook sharedAddressBook] people];
ABPerson *person = [people objectAtIndex:0];
NSArray *a0 = [[people valueForKey:@"phones"] valueForKey:@"label"];
NSArray *a1 = [people valueForKeyPath:@"phones.label"];
NSLog(@"a0 == a1: %d", [a0 isEqualTo:a1]);
NSArray *a2 = [[person valueForKey:@"phones"] valueForKey:@"label"];
NSArray *a3 = [person valueForKeyPath:@"phones.label"];
NSLog(@"a2 == a3: %d", [a2 isEqualTo:a3]);
NSLog(@"people count : %3ld", [people count]);
NSLog(@"people phones label count (valueForKey.valueForKey): %3ld", [a0 count]);
NSLog(@"people phones label count (valueForKeyPath): %3ld", [a1 count]);
NSLog(@"person phones label count (valueForKey.valueForKey): %3ld", [a2 count]);
NSLog(@"person phones label count (valueForKeyPath): %3ld", [a3 count]);
NSLog(@"----------------------------------------------------------");
NSLog(@"people phones label objectAtIndex:0 (valueForKeyPath): %@", [a1 objectAtIndex:0]);
NSLog(@"person phones label (valueForKey.valueForKey): %@", a2);
NSLog(@"person phones label (valueForKeyPath): %@", a3);
[pool release];
return 0;
}
This outputs:
2013-01-07 15:03:42.537 test[51919:303] a0 == a1: 1
2013-01-07 15:03:42.540 test[51919:303] a2 == a3: 0
2013-01-07 15:03:42.541 test[51919:303] people count : 200
2013-01-07 15:03:42.543 test[51919:303] people phones label count (valueForKey.valueForKey): 200
2013-01-07 15:03:42.544 test[51919:303] people phones label count (valueForKeyPath): 200
2013-01-07 15:03:42.545 test[51919:303] person phones label count (valueForKey.valueForKey): 2
2013-01-07 15:03:42.546 test[51919:303] person phones label count (valueForKeyPath): 0
2013-01-07 15:03:42.547 test[51919:303] ----------------------------------------------------------
2013-01-07 15:03:42.548 test[51919:303] people phones label objectAtIndex:0 (valueForKeyPath): (
mobile,
home
)
2013-01-07 15:03:42.549 test[51919:303] person phones label (valueForKey.valueForKey): (
mobile,
home
)
2013-01-07 15:03:42.551 test[51919:303] person phones label (valueForKeyPath): (null)
Why does [[person valueForKey:@"phones"] valueForKey:@"label"]
not equal [person valueForKeyPath:@"phones.label"]
? This is important because I want to use a predicate filter on all contacts using the full @"phone.label"
key path like so:
NSString *value = @"home";
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"ANY phones.label == %@", value];
NSArray *a4 = [people filteredArrayUsingPredicate:predicate];
In this example, a4
is always empty (using the predicate without the "ANY" flag still results in an empty filtered array). I believe this is because filteredArrayUsingPredicate:
is essentially evaluating each object (person) for valueForKeyPath:
and, as noted above, this does not work on the individual person objects. Is this correct?
If someone can shed some light on why valueForKeyPath:
on the ABPerson
object is failing, or offer a predicate for filtering the people array that returns the desired results, I'd be very appreciative.