In this section, we talk about the manipulation of multi-value properties.
1. Retrieving multi-value properties
To retrieve the value of a multi-value property, you use the same function you used to retrieve a single-value property, ABRecordCopyValue. The value returned is of type ABMultiValueRef which is declared as an alias to the generic CFTypeRef type.
The following shows how to retrieve the person's emails:
ABMultiValueRef emailsProperty =
ABRecordCopyValue(person, kABPersonEmailProperty);
Once we have the multi-value object, we can retrieve its values (in this case, the strings of emails), using the function ABMultiValueCopyArrayOfAllValues, which is declared as follows:
CFArrayRef ABMultiValueCopyArrayOfAllValues(ABMultiValueRef multiValue);
The function returns the values in an NSArray object. You can then iterate through this array and access the values.
Each value stored for a
multi-value property has a label identifying this value and a unique
identifier. The label can be any string value, and several values can
have the same label. Some of the generic labels defined are: kABWorkLabel, kABHomeLabel, and kABOtherLabel.
To retrieve the label for a given value, you can use the ABMultiValueCopyLabelAtIndex function. It returns an NSString object with the label of a given value. The function is declared as follows:
CFStringRef ABMultiValueCopyLabelAtIndex(
ABMultiValueRef multiValue, CFIndex index);
The first parameter is the
multi-value object and the second is the index of the value whose label
you want to retrieve. For example, the following retrieves the label
value for the email at index:
NSString *emailLabel =
(NSString*)ABMultiValueCopyLabelAtIndex(emailsProperty, index);
As usual, since the function has a "copy" in it, you are responsible for the object's memory.
The following are some of the multi-value property identifiers defined for the person record:
kABPersonAddressProperty. This identifier is used to access the person's address multi-value property. The property type is kABMultiDictionaryPropertyType, which means that each value of this property is an NSDictionary instance.
kABPersonDateProperty. This identifier is used to access all the dates associated with this person. Each value is an NSDate instance.
kABPersonPhoneProperty. This identifier is used to retrieve all phone numbers associated with the person. Each value is an NSString instance.
kABPersonURLProperty. This identifies all the URLs associated with this person. Each value is an NSString object.
Listing 1 shows a code fragment that retrieves all the emails of a given person and logs them labelled.
Example 1. Logging all the emails associated with a given person record.
//Access emails
ABMultiValueRef emailsProperty =
ABRecordCopyValue(person, kABPersonEmailProperty);
NSArray* emailsArray =
(NSArray*)ABMultiValueCopyArrayOfAllValues(emailsProperty);
NSLog(@"Emails:");
for (int index = 0; index< [emailsArray count]; index++){
NSString *email = [emailsArray objectAtIndex:index];
NSString *emailLabel =
(NSString*)ABMultiValueCopyLabelAtIndex(emailsProperty, index);
NSLog(@"%@: %@",
[emailLabel isEqualToString:(NSString*)kABWorkLabel]?@"Work":
[emailLabel isEqualToString:(NSString*)kABHomeLabel]?
@"Home":@"Other", email);
[emailLabel release];
}
CFRelease(emailsProperty);
[emailsArray release];
|
Listing 2 shows a code fragment that retrieves all the phone numbers of a given person and logs them labelled.
Example 2. Logging all the phone numbers associated with a given person record.
// Access phones
ABMultiValueRef phonesProperty =
ABRecordCopyValue(person, kABPersonPhoneProperty);
NSArray* phonesArray =
(NSArray*)ABMultiValueCopyArrayOfAllValues(phonesProperty);
NSLog(@"Phones:");
for (int index = 0; index< [phonesArray count]; index++){
NSString *aPhone = [phonesArray objectAtIndex:index];
NSString *phoneLabel =
(NSString*)ABMultiValueCopyLabelAtIndex(phonesProperty, index);
NSLog(@"%@: %@",
[phoneLabel isEqualToString:(NSString*)kABPersonPhoneMobileLabel]?
@"Mobile":@"Phone", aPhone);
[phoneLabel release];
}
CFRelease(phonesProperty);
[phonesArray release];
}
|
2. Setting multi-value properties
To modify a multi-value property, you need a mutable version of that property. If you want to create a brand new value, you use ABMultiValueCreateMutable
function, add the values and labels to it and set the corresponding
property with the new value. If, on the other hand, you want to modify
an existing multi-value, you retrieve this value, create a mutable copy
of it using the function ABMultiValueCreateMutableCopy, add to or delete from it, and set the value in the record.
Let's make these steps concrete through some examples. First, let's us look at creating a brand new multi-value.
We want to set a person's record with one work address. First, we create a mutable multi-value as follows:
ABMutableMultiValueRef multiValueAddress =
ABMultiValueCreateMutable(kABMultiDictionaryPropertyType);
Here, we are asking the address book framework to create a new multi-value object of kABMultiDictionaryPropertyType
type. As we mentioned before, this is the type of the street address
property (a person can have multiple addresses and each address is a
dictionary).
After that, we create one address and store its parts in a dictionary as follows:
NSMutableDictionary *theAddress =
[[[NSMutableDictionary alloc] init] autorelease];
[theAddress setValue:@"1600 Pennsylvania Avenue NW"
forKey:(NSString *)kABPersonAddressStreetKey];
[theAddress setValue:@"Washington"
forKey:(NSString *)kABPersonAddressCityKey];
[theAddress setValue:@"DC"
forKey:(NSString *)kABPersonAddressStateKey];
[theAddress setValue:@"20500"
forKey:(NSString *)kABPersonAddressZIPKey];
[theAddress setValue:@"us"
forKey:(NSString *)kABPersonAddressCountryCodeKey];
This is pretty
straight-forward dictionary creation. The keys used are defined by the
framework. You can set any or all of them as they are all optional.
Now that we have a new value, we need to add it to the multi-value and give it a label. The function to use is ABMultiValueAddValueAndLabel.
The first parameter of this function is the multi-value object, the
second is the value to add, and the third is the label. You can pass a
reference to an NSError object if you want to in the fourth parameter. The following shows the addition of the new address to the multi-value object:
ABMultiValueAddValueAndLabel(multiValueAddress, theAddress,
kABWorkLabel, NULL);
You can repeat these steps for
each additional address. Once you have what you want, you set the
multi-value property with the new value using our old friend ABRecordSetValue as follows:
ABRecordSetValue(person,kABPersonAddressProperty,multiValueAddress,NULL);
If, on the other hand, you
want to modify an existing multi-value, you need to retrieve it, create a
mutable copy of it, add to the mutable copy, and set it back to the
record. The following code fragment shows exactly that:
ABMultiValueRef streetAddressProperty =
ABRecordCopyValue(person, kABPersonAddressProperty);
ABMutableMultiValueRef mutableStreetAddressProperty =
ABMultiValueCreateMutableCopy(streetAddressProperty);
CFRelease(streetAddressProperty);
NSMutableDictionary *theAddress =
[[[NSMutableDictionary alloc] init] autorelease];
[theAddress setValue:@"1600 Pennsylvania Avenue NW"
forKey:(NSString *)kABPersonAddressStreetKey];
[theAddress setValue:@"Washington"
forKey:(NSString *)kABPersonAddressCityKey];
[theAddress setValue:@"DC"
forKey:(NSString *)kABPersonAddressStateKey];
[theAddress setValue:@"20500"
forKey:(NSString *)kABPersonAddressZIPKey];
[theAddress setValue:@"us"
forKey:(NSString *)kABPersonAddressCountryCodeKey];
ABMultiValueAddValueAndLabel(mutableStreetAddressProperty,
theAddress, kABHomeLabel, NULL);
ABRecordSetValue(person, kABPersonAddressProperty,
mutableStreetAddressProperty, NULL);
CFRelease(mutableStreetAddressProperty);