[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;
}
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
Related
I am new in IOS Programming. In my simple app I am storing first name,last name and phone number in NSMutableArray.I have a UITextfield where I get the phone number.I wanna search first name and last name according to mobile number.I am storing firstname, lastname and phone number in different Mutable arrays so how to search firstname and lastname in stored array.
This is my code
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
_firstname = [[NSMutableArray alloc]init];
_lastname = [[NSMutableArray alloc]init];
_number = [[NSMutableArray alloc]init];
_fullname = [[NSMutableArray alloc]init];
_arrContacts = [[NSMutableArray alloc]init];
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
ABAddressBookRef addressBook = ABAddressBookCreate( );
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
CFErrorRef *error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);
for(int i = 0; i < numberOfPeople; i++) {
ABRecordRef person = CFArrayGetValueAtIndex( allPeople, i );
NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
if (firstName != nil) {
[_firstname addObject:firstName];
}
else{
[_firstname addObject:#"Not Found"];
}
if (lastName != nil) {
[_lastname addObject:lastName];
}
else{
[_lastname addObject:#"Not Found"];
}
ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
[[UIDevice currentDevice] name];
NSArray *numbers = (__bridge NSArray *)(ABMultiValueCopyValueAtIndex(phoneNumbers, 0));
if (numbers != nil) {
[_arrContacts addObject:numbers];
NSLog(#"Numbers:%#",_arrContacts);
}
for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
NSString *phoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
_addressBookNum = [_addressBookNum stringByAppendingFormat: #":%#",phoneNumber];
}
}
// NSLog(#"AllNumber:%#",_addressBookNum);
}
else {
// Send an alert telling user to change privacy setting in settings app
}
CFRelease(addressBookRef);
NSLog(#"This is Name: %#",_firstname);
NSLog(#"This is LastName: %#",_lastname);
NSLog(#"Number: %#",_arrContacts);
}
Please suggest some idea to do this
Instead of saving name last name and number in different array store it in single array with dictionary. Suppose NSMutabelArray *details
create dictionary having three values name lastname and number, and add this dictoianry into array,
so it will be easy for you to further use
As i guess this is a very simple and basic question.
i am using #import <AddressBook/AddressBook.h> to get the contact info of device and i had successfully implemented the same.
Everythings work fine when i got one phone no and one email from each contact. But when i got multiple phone no's and multiple email then getting crash on my app. I guess i am not using the proper saving method.
So i really want to know how to save one name string and one array of email(different length) and phone no(different length) as group for a contact and same for all other. so that it would not be difficult to reproduce the result later on the detail screen
CFErrorRef *error = NULL;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);
for(int i = 0; i < numberOfPeople; i++) {
ABRecordRef person = CFArrayGetValueAtIndex( allPeople, i );
NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
[values1 addObject:[NSString stringWithFormat:#"%# %#",firstName,lastName]]; //values1 array to save name of contact
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
for (CFIndex j=0; j < ABMultiValueGetCount(emails); j++) {
NSString* email = (__bridge NSString*)ABMultiValueCopyValueAtIndex(emails, j);
NSLog(#"Email:%#", email);
[values2 addObject:[NSString stringWithFormat:#"%#",email]];
} //values2 array to save email associated to that contact
ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
NSString *phoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
NSLog(#"phone:%#", phoneNumber);
[values3 addObject:[NSString stringWithFormat:#"%#",phoneNumber]];
}//values3 array to save different phone no's associated to that contacts
NSDictionary *dict = [[NSDictionary alloc]init];
}
AS now i have three array with details contain details of one contact. Now how can merge these three array values to one so that it would be easy to save and fetch data for multiples or hundreds of contacts
ABAddressBookRef allPeople = ABAddressBookCreate();
CFArrayRef allContacts = ABAddressBookCopyArrayOfAllPeople(allPeople);
CFIndex numberOfContacts = ABAddressBookGetPersonCount(allPeople);
NSLog(#"numberOfContacts------------------------------------%ld",numberOfContacts);
for(int i = 0; i < numberOfContacts; i++){
NSString* name = #"";
NSString* phone = #"";
NSString* email = #"";
ABRecordRef aPerson = CFArrayGetValueAtIndex(allContacts, i);
ABMultiValueRef fnameProperty = ABRecordCopyValue(aPerson, kABPersonFirstNameProperty);
ABMultiValueRef lnameProperty = ABRecordCopyValue(aPerson, kABPersonLastNameProperty);
ABMultiValueRef phoneProperty = ABRecordCopyValue(aPerson, kABPersonPhoneProperty);\
ABMultiValueRef emailProperty = ABRecordCopyValue(aPerson, kABPersonEmailProperty);
NSArray *emailArray = (__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(emailProperty);
NSArray *phoneArray = (__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(phoneProperty);
if (fnameProperty != nil) {
name = [NSString stringWithFormat:#"%#", fnameProperty];
}
if (lnameProperty != nil) {
name = [name stringByAppendingString:[NSString stringWithFormat:#" %#", lnameProperty]];
}
if ([phoneArray count] > 0) {
if ([phoneArray count] > 1) {
for (int i = 0; i < [phoneArray count]; i++) {
phone = [phone stringByAppendingString:[NSString stringWithFormat:#"%#\n", [phoneArray objectAtIndex:i]]];
}
}else {
phone = [NSString stringWithFormat:#"%#", [phoneArray objectAtIndex:0]];
}
}
if ([emailArray count] > 0) {
if ([emailArray count] > 1) {
for (int i = 0; i < [emailArray count]; i++) {
email = [email stringByAppendingString:[NSString stringWithFormat:#"%#\n", [emailArray objectAtIndex:i]]];
}
}else {
email = [NSString stringWithFormat:#"%#", [emailArray objectAtIndex:0]];
}
}
NSLog(#"NAME : %#",name);
NSLog(#"PHONE: %#",phone);
NSLog(#"EMAIL: %#",email);
NSLog(#"\n");
}
You can see multiple contact and email in log if contact have.
I have this code for searching in the AddressBook and xcode suggests me that i have potential leaks in firstName, lastName, and another object that is unknown even to xcode. I tried to release the first 2 of them and the warning disappears but my app is crashing. Also i have a random crash on this line, i got it only once but appears in crashlytics for other people. Do you see anything wrong with my code? Thanks.
ABMultiValueRef phoneNumbers = ABRecordCopyValue((__bridge ABRecordRef)evaluatedObject, kABPersonPhoneProperty);
The code. It's purpose is to group all the contacts by the first letter of the name and to duplicate the contacts in case they have multiple numbers.
- (void)filterByKeyword:(NSString*)keyword completionBlock:(AddressBookSearchCompletionBlock)completionBlock {
dispatch_async(abQueue, ^{
[self.filteredContacts removeAllObjects];
// Iterate over original contacts
for (id evaluatedObject in self.allContacts) {
CFStringRef n = ABRecordCopyCompositeName((__bridge ABRecordRef)evaluatedObject);
NSString *name = (__bridge_transfer NSString*)n;
if (name == nil) {
RCLog(#"name is nil. skip this contact");
continue;
}
if (keyword == nil || [[name lowercaseString] rangeOfString:keyword].location != NSNotFound)
{
NSString *key = [[name substringToIndex:1] uppercaseString];
if (key == nil || [key isEqualToString:#"_"]) {
key = #"#";
}
NSMutableArray *arr = [self.filteredContacts objectForKey:key];
if (arr == nil) {
arr = [[NSMutableArray alloc] init];
}
ABMultiValueRef phoneNumbers = ABRecordCopyValue((__bridge ABRecordRef)evaluatedObject, kABPersonPhoneProperty);
signed long num = ABMultiValueGetCount(phoneNumbers);
if (num == 0) {
[arr addObject:evaluatedObject];
}
else {
for (CFIndex i = 0; i < num; i++) {
if (i > 0) {
// Create a separate contact with the alternative phone numbers of a contact
NSString* phoneNumber = (__bridge_transfer NSString*) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
NSString* phoneNumericNumber = [phoneNumber stringByTrimmingCharactersInSet:[[NSCharacterSet decimalDigitCharacterSet] invertedSet]];
if (phoneNumber != nil && phoneNumber.length > 0 && phoneNumericNumber.length > 0) {
ABRecordRef persona = ABPersonCreate();
CFStringRef firstName = ABRecordCopyValue((__bridge ABRecordRef)evaluatedObject, kABPersonFirstNameProperty);
CFStringRef lastName = ABRecordCopyValue((__bridge ABRecordRef)evaluatedObject, kABPersonLastNameProperty);
ABRecordSetValue (persona, kABPersonFirstNameProperty, firstName, nil);
ABRecordSetValue (persona, kABPersonLastNameProperty, lastName, nil);
ABMutableMultiValueRef multiPhone = ABMultiValueCreateMutable(kABMultiStringPropertyType);
bool didAddPhone = ABMultiValueAddValueAndLabel(multiPhone, (__bridge CFTypeRef)(phoneNumber), kABPersonPhoneMobileLabel, NULL);
if (didAddPhone){
ABRecordSetValue(persona, kABPersonPhoneProperty, multiPhone, nil);
}
if (ABPersonHasImageData((__bridge ABRecordRef)evaluatedObject)) {
ABPersonSetImageData(persona, ABPersonCopyImageDataWithFormat((__bridge ABRecordRef)evaluatedObject, kABPersonImageFormatThumbnail), nil);
}
[arr addObject:(__bridge id)persona];
CFRelease(multiPhone);
CFRelease(persona);
}
}
else {
[arr addObject:evaluatedObject];
}
}
}
CFRelease(phoneNumbers);
[self.filteredContacts setObject:arr forKey:key];
RCLog(#"%i contacts for key %#", arr.count, key);
}
}
self.filteredKeys = [[self.filteredContacts allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
});
}
I am tring to get email address of ABRecordRef like this:
ABRecordRef ref = CFArrayGetValueAtIndex( allPeople, i );
NSString *email = [(NSString*) ABRecordCopyValue( ref, kABPersonEmailProperty ) autorelease];
NSLog(#"%#", email);
It returning this:
_$!<Home>!$_ (0x6840af0) - test#test.com (0x6840cc0)
What's this stuff around the email? and how can I get rid of it?Thanks.
kABPersonEmailProperty is of type kABMultiStringPropertyType. There is no single email address property, a person might have an email address for work, one for home, etc.
You can get an array of all email addresses by using ABMultiValueCopyArrayOfAllValues:
ABMultiValueRef emailMultiValue = ABRecordCopyValue(ref, kABPersonEmailProperty);
NSArray *emailAddresses = [(NSArray *)ABMultiValueCopyArrayOfAllValues(emailMultiValue) autorelease];
CFRelease(emailMultiValue);
To get the labels of the email addresses, use ABMultiValueCopyLabelAtIndex. "_$!<Home>!$" is a special constant that's defined as kABHomeLabel, there's also kABWorkLabel.
Basically more details for #omz answer. Here is the code I used that extracts home email and the name of the person:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
for (CFIndex i = 0; i < ABMultiValueGetCount(emails); i++) {
NSString *label = (__bridge NSString *) ABMultiValueCopyLabelAtIndex(emails, i);
if ([label isEqualToString:(NSString *)kABHomeLabel]) {
NSString *email = (__bridge NSString *) ABMultiValueCopyValueAtIndex(emails, i);
_emailTextField.text = email;
}
}
CFRelease(emails);
NSString *first = (__bridge NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *last = (__bridge NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
if (first && first.length > 0 && last && last.length > 0) {
_nicknameTextField.text = [NSString stringWithFormat:#"%# %#", first, last];
} else if (first && first.length > 0) {
_nicknameTextField.text = first;
} else {
_nicknameTextField.text = last;
}
[self dismissModalViewControllerAnimated:YES];
return NO;
}
Try out this......
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
// Display only a person's phone, email, and birthdate
NSArray *displayedItems = [NSArray arrayWithObjects:
[NSNumber numberWithInt:kABPersonPhoneProperty],
[NSNumber numberWithInt:kABPersonEmailProperty],
[NSNumber numberWithInt:kABPersonBirthdayProperty], nil];
picker.displayedProperties = displayedItems;
Overview: I am trying to get the phone number and full name when people use the peoplePicker and clicks on the name. Then, I'd like to display the full name on a textfield and save the phone number as a string. Using the ph num and name, I intend to use that as a unique identification. I don't want to use the unique id of ABRecord because sometimes i have duplicates on my contacts especially when I sync it with google, etc...
If I understand correctly, I need to use this delegate method
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
Using the above, I can get the full name to display on the text field as scuh
textField.text = ABRecordCopyCompositeName(person);
But, I don't know how to get the ph number. For me to get the ph number, I have to use the other delegate method:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
ABMultiValueRef phoneProperty = ABRecordCopyValue(person,property);
NSString *phone = (NSString *)ABMultiValueCopyValueAtIndex(phoneProperty,identifier);
However, I don't like this because then when the user clicks on the name on the address book, it shows the detail with the ph number, email, etc and the user has to click on the ph number. What I want is from the first screen, the user clicks on the name and the name gets displayed as a textField and the ph number is saved as a string somewhere.
I use a method to save all persons with emails in a array and then display the array in a table view:
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
NSMutableArray *mArr = [[NSMutableArray alloc]init];
for( int i = 0 ; i < nPeople ; i++ )
{
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
NSString *preName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
NSString *postName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
ABMultiValueRef emails = (ABMultiValueRef) ABRecordCopyValue(person, kABPersonEmailProperty);
int ecount = ABMultiValueGetCount(emails);
for (int i = 0; i < ecount; i++) {
NSMutableDictionary *dd = [[NSMutableDictionary alloc]init];
[dd setValue:[NSString stringWithFormat:#"%# %#", preName, postName] forKey:#"name"];
NSString *emailID = (NSString *)ABMultiValueCopyValueAtIndex(emails, i);
[dd setValue:emailID forKey:#"mail"];
//NSLog(#"inside loop %# %# %#", preName, postName, emailID);
[emailID release];
[mArr addObject:dd];
[dd release];
}
}
emailArray = [[NSArray alloc]initWithArray:mArr];
[mArr release];
If you want to get just the first email and phone number use this code. This is for iOS 5.0 with Xcode 4.2
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
//NSLog(#"Went here 1 ...");
NSString *nameStr = (__bridge NSString *)ABRecordCopyCompositeName(person);
ABMultiValueRef emails = (ABMultiValueRef) ABRecordCopyValue(person, kABPersonEmailProperty);
ABMultiValueRef phones = (ABMultiValueRef) ABRecordCopyValue(person, kABPersonPhoneProperty);
NSString *emailStr = (__bridge NSString *)ABMultiValueCopyValueAtIndex(emails, 0);
NSString *phoneStr = (__bridge NSString *)ABMultiValueCopyValueAtIndex(phones, 0);
//strip number from brakets
NSMutableString *tmpStr1 = [NSMutableString stringWithFormat:#"%#", phoneStr];
NSString *strippedStr1 = [tmpStr1 stringByReplacingOccurrencesOfString:#" " withString:#""];
NSCharacterSet *doNotWant = [NSCharacterSet characterSetWithCharactersInString:#"()-"];
strippedStr1 = [[strippedStr1 componentsSeparatedByCharactersInSet: doNotWant] componentsJoinedByString: #""];
NSLog(#"nameStr: %# ... emailStr: %# ... phoneStr: %# ...", nameStr, emailStr,strippedStr1);
//dismiss
[self dismissModalViewControllerAnimated:YES];
return NO;
}