9.1. Address Book Access

The interfaces for accessing the address book and individual contact data are C-based functions, which pass references around for various address book objects. The base object for managing address book entries is an ABRecord. An ABRecord can represent either a single person (ABPerson) or a group (ABGroup). Whenever a record is selected in the address book's UI or returned from a query using the framework, the system returns a pointer to an ABRecord, represented as ABRecordRef. A majority of your interaction with the address book API will involve working with ABRecordRef references.

An ABRecordRef, which represents a reference to exactly one contact, has many functions used to read or write information. You'll learn about many of these functions in this chapter. The most generic functions used to interact with records follow:



ABRecordID ABRecordGetRecordID(ABRecordRef record);

Returns an ABRecordID; a 32-bit integer representing the ID of the record within the database. This can be useful when you need a primary key to keep track of multiple records. You can also query by record ID, as you'll learn in this chapter.



ABRecordType ABRecordGetRecordType(ABRecordRef record);

Returns the type of entity that this record represents. Possible values for ABRecordType can correspond to either a person (kABPersonType) or a group (kABGroupType).



CFStringRef ABRecordCopyCompositeName(ABRecordRef record);

Returns the full name of the person or group. All CFStringRef data types can be cast to an NSString *, giving you access to an NSString object containing the record name. For example, NSString *name = (NSString *) ABRecordCopyCompositeName(record);.

9.1.1. Top-Level Address Book Functions

You must initialize the address book before you can read from it or write to it. Use the ABAddressBookCreate function to obtain a handle to the address book:

#import <AddressBook/AddressBook.h>

ABAddressBookRef ab = ABAddressBookCreate();

You must explicitly save any changes that you make to the address book. When you're finished editing and want to commit changes, invoke the ABAddressBookSave function to do this:

CFErrorRef error;
BOOL success = ABAddressBookSave(ab, &error);

If you're not sure whether the address book needs to be saved, use the ABAddressBoo⁠kHa⁠sUnsavedChanges function:

BOOL hasUnsavedChanges = ABAddressBookHasUnsavedChanges(ab);

To add or remove records, use the ABAddressBoo⁠kAddRecord and ABAddressBookRe⁠move⁠Record functions:

CFErrorRef error;
BOOL success = ABAddressBookAddRecord(ab, myRecord, &error);
BOOL success = ABAddressBookRemoveRecord(ab, myRecord, &error);

9.1.2. Querying the Address Book

The Address Book framework provides only basic query functionality. Functions exist to query multiple records by name, or individual records by specific record IDs.

To obtain a count of the total number of records in the address book, use the ABAddressBoo⁠kGetPersonCount function. This function returns a CFIndex, which is typed to a 32-bit integer:

CFIndex count = ABAddressBookGetPersonCount(ab);
NSLog(@"%ld total entries in the address book", count);

Two functions exist to query multiple address book records. Both return a CFArrayRef, which can be cast to an NSArray *; that is, a pointer to an NSArray object.

To obtain a list of all contacts in the database, use the ABAddressBookCopyArrayOfAllPeople function:

NSArray *array = (NSArray *)ABAddressBookCopyArrayOfAllPeople(ab);
NSLog(@"Retrieved %d contacts\n", [ array count ]);

To search the contact list for a specific name, use the ABAddressBookCopyPeopleWithName function. You can search by first name, last name, or both. All matching records will be returned in the array:

NSArray *array = (NSArray *)ABAddressBookCopyPeopleWithName(ab,
    CFSTR("John Appleseed));

As the function names imply, the objects being returned are not the actual objects in the address book, but rather copies. To access the individual records within the array, use the NSArray class's objectAtIndex method:

ABRecordRef record = [ people objectAtIndex: 0 ];

In addition to querying multiple contacts, if you know the record ID of the contact you'd like to load, you can load it directly using the ABAddressBookGetPersonWithRe⁠cor⁠dID function:

ABRecordRef record = ABAddressBookGetPersonWithRecordID(ab, recordId);

9.1.3. Creating Records

To create a new contact, use the ABPersonCreate function. This will give you an empty record you can later add information to:

ABRecordRef record = ABPersonCreate();

9.1.4. Working with Records

Once you have an ABRecordRef in hand, you'll determine whether the record is that of a person or a group so you can access more specific information. First name, last name, and other pieces of information are all referred to as properties. An entity only has properties if it is an ABPerson record, and a varying amount of information will be available for each record.

To query information for a given record, use the ABRecordCopyValue function. The function prototype follows:

CFTypeRef ABRecordCopyValue(ABRecordRef record, ABPropertyID property);

When called, the ABRecordCopyValue function copies the property you specify and returns a reference:

CFStringRef firstName = ABRecordCopyValue(myRecord, kABPersonFirstNameProperty);

                                          

Because the kABPersonFirstNameProperty property is a CFStringRef, you may choose to cast it to an NSString *:

NSString *firstName = (NSString *) ABRecordCopyValue(myRecord,
    kABPersonFirstNameProperty);

Just as a CFStringRef can be cast to an NSString *, any property returning a CFDateRef can be cast to an NSDate *:

NSDate *birthday = (NSDate *) ABRecordCopyValue(myRecord,
    kABPersonBirthdayProperty);

The ABPropertyID you specified above is a value corresponding to the information you're looking up in the record. Properties for the ABPerson object follow. Because the data type returned by the ABRecordCopyValue function is a generic CFTypeRef, the resulting value can be cast to a more specific data type designed for that property.

Property Description Data type
kABPersonFirstNameProperty First Name CFStringRef
kABPersonLastNameProperty Last name CFStringRef
kABPersonMiddleNameProperty Middle name CFStringRef
kABPersonPrefixProperty Surname; "Sir" CFStringRef
kABPersonSuffixProperty Suffix, "Jr." or "Sr." CFStringRef
kABPersonNicknameProperty Nickname CFStringRef
kABPersonFirstNamePhoneticProperty First name; phonetic CFStringRef
kABPersonLastNamePhoneticProperty Last name; phonetic CFStringRef
kABPersonMiddleNamePhoneticProperty Mid name; phonetic CFStringRef
kABPersonOrganizationProperty Company name CFStringRef
kABPersonJobTitleProperty Job title CFStringRef
kABPersonDepartmentProperty Department CFStringRef
kABPersonBirthdayProperty Birthday CFDateRef
kABPersonNoteProperty Notes CFStringRef
kABPersonCreationDateProperty Creation date CFDateRef
kABPersonModificationDateProperty Date last modified CFDateRef
kABPersonKindProperty Contact type Enumeration

9.1.4.1. Writing properties

To write a property to an address book record, use the ABRecordSetValue function:

CFErrorRef error;
CFStringRef nickname = CFSTR("Sparky");
BOOL success = ABRecordSetValue(record, kABPersonNicknameProperty,
    nickname, &error);

To delete a property, use the ABRecordRemoveValue function:

CFErrorRef error;
BOOL success = ABRecordRemoveValue(myRecord, kABPersonNicknameProperty, &error);

                                          

When you have finished editing a record, don't forget to save the address book:

CFErrorRef error;
BOOL success = ABAddressBookSave(ab, &error);

9.1.5. Multivalue Properties

In addition to the properties listed previously, certain values for a record may contain multiple values. Multivalue properties are handled using an indexing mechanism, where the total number of values is first queried, followed by a call to retrieve an entry at a particular index. A pointer to the multivalue data is first obtained using the ABRecordCopyValue method previously described, and is cast to an ABMultiValueRef:

ABMultiValueRef phoneNumbers = ABRecordCopyValue(myRecord, kABPersonPhoneProperty);

                                          

You can then use the reference to determine the number of values and to retrieve individual values by index. The ABMultiValueGetCount function returns the number of items, and the ABMultiValueCopyValueAtIndex function copies the item at the index you specify:

NSMutableArray *phoneNumbersList = [ [ NSMutableArray alloc ] init ];

CFIndex nPhoneNumbers = ABMultiValueGetCount(phoneNumbers);
for(int i=0;i<nPhoneNumbers;i++) {
    NSString *phoneNumber = (NSString *)ABMultiValueCopyValueAtIndex(
        phoneNumbers, i);
    [ phoneNumbersList addObject: phoneNumber ];
    [ phoneNumber release ];
}

The following data types describe an individual entry within the multivalue properties listed.

Property Description Data type
kABPersonEmailProperty Email addresses CFStringRef
kABPersonAddressProperty Address CFDictionaryRef
kABPersonDateProperty Associated dates CFDateRef
kABPersonPhoneProperty Phone numbers CFStringRef
kABPersonInstantMessageProperty IM IDs CFDictionaryRef
kABPersonURLProperty Website URLs CFStringRef
kABPersonRelatedNamesProperty Related names CFStringRef

In addition to the actual entries within a multivalue property, entries are also given a label. The label describes the type of entry being returned. For example, labels for individual phone numbers might specify that the number is a home or mobile number. Labels for addresses might describe home or work addresses. To query the label for a given entry, use the ABMultiValueCopyLabelAtIndex function:

CFStringRef label = ABMultiValueCopyLabelAtIndex(phoneNumbers, i);

Certain properties have a defined set of labels. The following CFStringRef labels are specified in the ABPerson.h prototypes:



kABPersonDateProperty

kABPersonAnniversaryLabel


kABPersonPhoneProperty

kABPersonPhoneMobileLabel
kABPersonPhoneMainLabel
kABPersonPhoneHomeFAXLabel
kABPersonPhoneWorkFAXLabel
kABPersonPhonePagerLabel


kABPersonInstantMessageProperty

Applies to the kABPersonInstantMessageServiceKey key within the dictionary:

kABPersonInstantMessageServiceYahoo
kABPersonInstantMessageServiceJabber
kABPersonInstantMessageServiceMSN
kABPersonInstantMessageServiceICQ
kABPersonInstantMessageServiceAIM


kABPersonURLProperty

kABPersonHomePageLabel


kABPersonRelatedNamesProperty

kABPersonFatherLabel
kABPersonMotherLabel
kABPersonParentLabel
kABPersonBrotherLabel
kABPersonSisterLabel
kABPersonChildLabel
kABPersonFriendLabel
kABPersonSpouseLabel
kABPersonPartnerLabel
kABPersonAssistantLabel
kABPersonManagerLabel

Many properties use a generic set of labels for work, home, and other locations. These generic labels follow:

kABWorkLabel
kABHomeLabel
kABOtherLabel
9.1.5.1. Writing multivalue entries

In order to add a value to an existing multivalue property, you must first copy the multivalue dictionary from the record. You'll then operate on the new copy by adding a new value/label pair using the ABMultiValueAddValueAndLabel function. Finally, use the ABRecordSetValue function to write the entire dictionary back to the address book record, effectively replacing the entire multivalue property. In the example to follow, a new URL is added to the kABPersonURLProperty property:

CFErrorRef error;
ABMultiValueRef URLs = ABRecordCopyValue(myRecord, kABPersonURLProperty);
ABMutableMultiValueRef copyOfURLs = ABMultiValueCreateMutableCopy(URLs);
ABMultiValueAddValueAndLabel(copyOfURLs, "http://www.oreilly.com",
    kABPersonHomePageLabel, NULL);
ABRecordSetValue(myRecord, kABPersonURLProperty, copyOfURLs, &error);

To delete a value/label pair, use the ABMultiValueRemoveValueAndLabelAtIndex function:

CFErrorRef error;
ABMultiValueRef URLs = ABRecordCopyValue(myRecord, kABPersonURLProperty);
ABMutableMultiValueRef copyOfURLs = ABMultiValueCreateMutableCopy(URLs);
ABMultiValueRemoveValueAndLabelAtIndex(copyOfURLs, 0);
ABRecordSetValue(myRecord, kABPersonURLProperty, copyOfURLs, &error);

Be sure to save the record using the ABAddressBookSave function:

ABAddressBookSave(ab, &error);

9.1.6. Working with Dictionaries

Address book records use dictionaries to describe addresses and instant messenger accounts. These dictionaries are embedded within multivalue entries. To access these, copy the value and cast it to an NSDictionary *. From here, you'll be able to access the dictionary using a set of predefined keys:

ABMultiValueRef addresses = ABRecordCopyValue(record, kABPersonAddressProperty);

CFIndex nAddresses = ABMultiValueGetCount(addresses);
NSLog(@"%d addresses\n", nAddresses);

NSDictionary *address = ABMultiValueCopyValueAtIndex(addresses, 0);
for (id key in address)
{
    NSLog(@"key: %@, value: %@", key, [ address objectForKey: key ]);
}

                                          

The following keys are used for dictionary properties stored in the address book:



kABPersonAddressProperty

kABPersonAddressStreetKey
kABPersonAddressCityKey
kABPersonAddressStateKey
kABPersonAddressZIPKey
kABPersonAddressCountryKey
kABPersonAddressCountryCodeKey


kABPersonInstantMessengerProperty

kABPersonInstantMessageServiceKey
kABPersonInstantMessageUsernameKey

9.1.7. Image Data

Some contacts may have an image associated with them. Use the ABPersonCopyImageData function to obtain the image data, returned as a CFDataRef. You can cast this to an NSData *, which can initialize a UIImage object. You learned about the UIImage class in previous chapters, and will read more about them in Chapter 10.

if (ABPersonHasImageData(myRecord)) {
    UIImage *addressBookImage = [ UIImage imageWithData:
        (NSData *) ABPersonCopyImageData(myRecord)
    ];
}

9.1.8. Further Study

  • You can find a great wealth of information about address book properties and functions in the prototypes found inside the AddressBook.framework header directories. You'll find these deep within /Developer/Platforms/iPhoneOS.plat⁠form/ in your SDK headers.