Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ABRecordRef fields don't seem to be populated?

[background: iOS 7, Xcode 5, both up to date as of Feb 2014. Test data is address book entries with multiple phone numbers and multiple addresses in addition to basic contact info, on an iPhone 5 (real device, not simulator)]

My goal is to use the AddressBookUI methods to allow a user to specify a contact, then use the various fields (addresses, phone numbers, etc) to populate a GUI in my code. The ABPeoplePickerNavigationController is the standard mechanism to allow the user to pick a contact by name. Doing so results in this delegate method being called:

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker 
shouldContinueAfterSelectingPerson:(ABRecordRef)person

However if I examine the person record at this point, none of the multi-value fields have any data. So I extracted the RecordID, retrieved that, and the resulting ABRecordRef also has no multi-value fields filled in.

If I return YES from the delegate method, another UI is shown to the user with contact details displayed. Touching any field results in this delegate method call

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person 
property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier

and that ABRecordRef has all the multi value fields filled in.

I can't find any information about the records being lazily loaded, or there being either a required delay, or permissions issue that would prevent the fields from being filled in. And the code I am using to examine the records is a method, so the exact same code which finds the values in the second instance fails to find it in the first.

Any suggestions about what may be going on, or how I can retrieve the full records without displaying the second UI to the user?

I am using Apple's QuickContacts sample code. Here are the additions and changes I have made.

+(NSMutableDictionary *)convertABRecordRef:(ABRecordRef)person
{
    // Initialize a mutable dictionary and give it initial values.
    NSMutableDictionary *contactInfoDict = [[NSMutableDictionary alloc] initWithCapacity:12];

    // Use a general Core Foundation object.
    CFTypeRef generalCFObject = ABRecordCopyValue(person, kABPersonFirstNameProperty);

    ABRecordID foundId = ABRecordGetRecordID(person);
    NSNumber *personIDNum = [NSNumber numberWithInteger:foundId];
    [contactInfoDict setObject:personIDNum forKey:@"recordID"];


    // Get the first name.
    if (generalCFObject)
    {
        [contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:@"firstName"];
        CFRelease(generalCFObject);
    }

    // Get the last name.
    generalCFObject = ABRecordCopyValue(person, kABPersonLastNameProperty);
    if (generalCFObject) {
        [contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:@"lastName"];
        CFRelease(generalCFObject);
    }

    generalCFObject = ABRecordCopyValue(person, kABPersonOrganizationProperty);
    if (generalCFObject) {
        [contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:@"companyName"];
        CFRelease(generalCFObject);
    }

    //ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
    //NSArray *numbers = (NSArray *)ABMultiValueCopyArrayOfAllValues(phones);


    // Get the phone numbers as a multi-value property.
    ABMultiValueRef phonesRef = ABRecordCopyValue(person, kABPersonPhoneProperty);
    CFIndex phoneCount = ABMultiValueGetCount(phonesRef);
    for (CFIndex i=0; i<ABMultiValueGetCount(phonesRef); i++) {

        CFStringRef currentPhoneLabel = ABMultiValueCopyLabelAtIndex(phonesRef, i);
        CFStringRef currentPhoneValue = ABMultiValueCopyValueAtIndex(phonesRef, i);

        if (CFStringCompare(currentPhoneLabel, kABPersonPhoneMobileLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentPhoneValue forKey:@"mobileNumber"];
        }

        if (CFStringCompare(currentPhoneLabel, kABHomeLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentPhoneValue forKey:@"homeNumber"];
        }

        if (CFStringCompare(currentPhoneLabel, kABWorkLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentPhoneValue forKey:@"workNumber"];
        }

        CFRelease(currentPhoneLabel);
        CFRelease(currentPhoneValue);
    }
    CFRelease(phonesRef);


    // Get the e-mail addresses as a multi-value property.
    ABMultiValueRef emailsRef = ABRecordCopyValue(person, kABPersonEmailProperty);
    for (int i=0; i<ABMultiValueGetCount(emailsRef); i++)
    {
        CFStringRef currentEmailLabel = ABMultiValueCopyLabelAtIndex(emailsRef, i);
        CFStringRef currentEmailValue = ABMultiValueCopyValueAtIndex(emailsRef, i);

        if (CFStringCompare(currentEmailLabel, kABHomeLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentEmailValue forKey:@"homeEmail"];
        }

        if (CFStringCompare(currentEmailLabel, kABWorkLabel, 0) == kCFCompareEqualTo) {
            [contactInfoDict setObject:(__bridge NSString *)currentEmailValue forKey:@"workEmail"];
        }

        CFRelease(currentEmailLabel);
        CFRelease(currentEmailValue);
    }
    CFRelease(emailsRef);


    // Get the first street address among all addresses of the selected contact.
    ABMultiValueRef addressRef =  ABRecordCopyValue(person, kABPersonAddressProperty);
    if (ABMultiValueGetCount(addressRef) > 0)
    {
        CFIndex numberOfAddresses = ABMultiValueGetCount(addressRef);
        for (CFIndex i=0; i<numberOfAddresses; i++)
        {
            CFStringRef label = ABMultiValueCopyLabelAtIndex(addressRef, i);
            if (label)
            {
                if (CFEqual(label, kABHomeLabel))
                {
                    NSDictionary *addressDict = (__bridge NSDictionary *)ABMultiValueCopyValueAtIndex(addressRef, 0);

                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressStreetKey] forKey:@"homeAddress"];
                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressZIPKey] forKey:@"homeZipCode"];
                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressCityKey] forKey:@"homeCity"];

                }
                else if (CFEqual(label, kABWorkLabel))
                {
                    NSDictionary *addressDict = (__bridge NSDictionary *)ABMultiValueCopyValueAtIndex(addressRef, 0);

                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressStreetKey] forKey:@"workAddress"];
                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressZIPKey] forKey:@"workZipCode"];
                    [contactInfoDict setObject:[addressDict objectForKey:(NSString *)kABPersonAddressCityKey] forKey:@"workCity"];
                }
                CFRelease(label);
            }
        }
    }
    CFRelease(addressRef);


    // If the contact has an image then get it too.
    if (ABPersonHasImageData(person)) {
        NSData *contactImageData = (__bridge NSData *)ABPersonCopyImageDataWithFormat(person, kABPersonImageFormatThumbnail);

        [contactInfoDict setObject:contactImageData forKey:@"image"];
    }


    return contactInfoDict;
}



// Displays the information of a selected person
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
    // only returns a few fields, and none of the multi value ones  :-(
    NSMutableDictionary *results = [QuickContactsViewController convertABRecordRef:person];

    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
    ABRecordID foundId = ABRecordGetRecordID(person);
    ABRecordRef fullPerson = ABAddressBookGetPersonWithRecordID(addressBook, foundId);

    // also only returns a few fields!?
    NSMutableDictionary *selectedFromID = [QuickContactsViewController convertABRecordRef:fullPerson];


    return YES;
}

// Does not allow users to perform default actions such as dialing a phone number, when they select a person property.
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person 
                                property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
    // returns all simple and multi-value fields!?   
    NSMutableDictionary *results = [QuickContactsViewController convertABRecordRef:person];

    return NO;
}

EDIT: Adding my solution (thanks Thorsten!).

- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
    NSArray *allPersonRecords = (NSArray *)CFBridgingRelease(ABPersonCopyArrayOfAllLinkedPeople(person));
    NSLog(@"Count Linked People: %i", allPersonRecords.count);

    NSMutableDictionary *results = [[NSMutableDictionary alloc]initWithCapacity:12];
    for (int x=0; x<[allPersonRecords count]; x++)
    {
        ABRecordRef user = CFBridgingRetain([allPersonRecords objectAtIndex:x]);
        NSMutableDictionary *userFromArray = [QuickContactsViewController convertABRecordRef:user];
        [results addEntriesFromDictionary:userFromArray];  // mush them together
        CFRelease(user);
    }

    NSLog(@"finished! Total number of contact fields found:%d", [results count]);
    // do something with the data here...

    return NO;
}
like image 511
software evolved Avatar asked Feb 18 '14 05:02

software evolved


1 Answers

One person can consist of multiple people. Iterate through all people to get your data:

NSArray *people = (NSArray *)CFBridgingRelease(ABPersonCopyArrayOfAllLinkedPeople(person));
...
ABRecordRef user = CFBridgingRetain([people objectAtIndex:i]);
...
valuesRef = ABRecordCopyValue(user, kABPersonPhoneProperty);
valuesCount = 0;
if (valuesRef != nil) valuesCount = ABMultiValueGetCount(valuesRef);
...

HTH

like image 192
Thorsten Avatar answered Nov 06 '22 15:11

Thorsten