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;
}
Related
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'm using ABAddressBookCreateWithOptions and ABAddressBookCopyArrayOfAllPeople to achieve all contacts' informations.
I can get person's full name, emails and phone numbers like that:
addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
people = ABAddressBookCopyArrayOfAllPeople(addressBook);
for (CFIndex i = 0; i < CFArrayGetCount(people); i++)
{
ABRecordRef person = CFArrayGetValueAtIndex(people, i);
////get full name////
NSString *fullname = #"";
if (ABRecordCopyValue(person, kABPersonFirstNameProperty)!=NULL){
fullname = [NSString stringWithFormat:#"%# ", ABRecordCopyValue(person, kABPersonFirstNameProperty)];
}
if (ABRecordCopyValue(person, kABPersonMiddleNameProperty)!=NULL){
fullname = [NSString stringWithFormat:#"%#%# ", fullname,ABRecordCopyValue(person, kABPersonMiddleNameProperty)];
}
if (ABRecordCopyValue(person, kABPersonLastNameProperty)!=NULL){
fullname = [NSString stringWithFormat:#"%#%#", fullname,ABRecordCopyValue(person, kABPersonLastNameProperty)];
}
fullname = [fullname stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"fullname: %#",fullname);
////get phone numbers////
ABMultiValueRef phonenumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
if (ABMultiValueGetCount(phonenumbers)>0)
{
for (CFIndex j=0; j < ABMultiValueGetCount(phonenumbers); j++)
{
NSString *phonenumber = (NSString*)CFBridgingRelease(ABMultiValueCopyValueAtIndex(phonenumbers, j));
NSLog(#"phone number: %#",phonenumber);
}
}
CFRelease(phonenumbers);
////get emails////
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
if (ABMultiValueGetCount(emails)>0)
{
for (CFIndex j=0; j < ABMultiValueGetCount(emails); j++)
{
NSString *email = (NSString*)CFBridgingRelease(ABMultiValueCopyValueAtIndex(emails, j));
NSLog(#"email: %#",email);
}
}
CFRelease(emails);
}
CFRelease(addressBook);
CFRelease(people);
Everything works perfectly. But I need to create a JSON object with these informations like that:
[{"name":"Christine Work","phone_numbers":["+99023424234"]},{"name":"Alex Bla","phone_numbers":["+135352125262","+13433452347"],"email_addresses":["bla#bla.com","bla2#bla2.com"]}]
Scenario: If person has email address, than add it to json object, if not, not incude it in json.
If person has more than one phone number or more than one email address add all of them to json.
I'm stuck right here. I know how can I create a json object with NSDictionary :
NSError *error;
NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:
#"alex", #"name",
#"+90225252", #"phones",
nil];
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonString = [[NSString alloc] initWithData:info encoding:NSUTF8StringEncoding];
but how can I integrate this code to my scenario in the loop.
Give this a go. Added the necessary code to create the JSON on top of your code. If there are no phone numbers or emails for a contact, NSNull is added for that key. Make sure to check for it when you are pulling the data out of the JSON. Didn't build the code, so let me know if you run into any errors.
NSMutableArray *usersArray = [[NSMutableArray alloc] init];
NSMutableDictionary *singleUserDictionary = [[NSMutableDictionary alloc] init];
NSMutableArray *phoneNumbersArray = [[NSMutableArray alloc] init];
NSMutableArray *emailArray = [[NSMutableArray alloc] init];
for (CFIndex i = 0; i < CFArrayGetCount(people); i++) {
ABRecordRef person = CFArrayGetValueAtIndex(people, i);
////get full name////
NSString *fullname = #"";
if (ABRecordCopyValue(person, kABPersonFirstNameProperty)!=NULL){
fullname = [NSString stringWithFormat:#"%# ", ABRecordCopyValue(person, kABPersonFirstNameProperty)];
}
if (ABRecordCopyValue(person, kABPersonMiddleNameProperty)!=NULL){
fullname = [NSString stringWithFormat:#"%#%# ", fullname,ABRecordCopyValue(person, kABPersonMiddleNameProperty)];
}
if (ABRecordCopyValue(person, kABPersonLastNameProperty)!=NULL){
fullname = [NSString stringWithFormat:#"%#%#", fullname,ABRecordCopyValue(person, kABPersonLastNameProperty)];
}
fullname = [fullname stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"fullname: %#",fullname);
[singleUserDictionary setObject:fullname forKey:#"name"];
////get phone numbers////
ABMultiValueRef phonenumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
if (ABMultiValueGetCount(phonenumbers)>0)
{
for (CFIndex j=0; j < ABMultiValueGetCount(phonenumbers); j++)
{
NSString *phonenumber = (NSString*)CFBridgingRelease(ABMultiValueCopyValueAtIndex(phonenumbers, j));
NSLog(#"phone number: %#",phonenumber);
[phoneNumbersArray addObject:phonenumber];
}
}
else
[phoneNumbersArray addObject:[NSNull null]];
[singleUserDictionary setObject:phoneNumbersArray forKey:#"phone_numbers"];
CFRelease(phonenumbers);
////get emails////
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
if (ABMultiValueGetCount(emails)>0)
{
for (CFIndex j=0; j < ABMultiValueGetCount(emails); j++)
{
NSString *email = (NSString*)CFBridgingRelease(ABMultiValueCopyValueAtIndex(emails, j));
NSLog(#"email: %#",email);
[emailArray addObject:email];
}
}
else
[emailArray addObject:[NSNull null]];
[singleUserDictionary setObject:emailArray forKey:#"email_addresses"];
CFRelease(emails);
[usersArray addObject:[NSDictionary dictionaryWithDictionary:singleUserDictionary]];
[singleUserDictionary removeAllObjects];
[phoneNumbersArray removeAllObjects];
[emailArray removeAllObjects];
}
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:usersArray options:0 error:&error];
if (error) {
//json success
}
CFRelease(addressBook);
CFRelease(people);
Pseudo-code:
Create outer NSMutableArray.
For each person --
create NSMutableDictionary
insert name
create NSMutableArray and insert into it phone numbers
insert the array into dictionary as "phone_numbers"
repeat for email addresses (if present)
insert dictionary into array
Run outer array through NSJSONSerialization to serialize into an NSData object
[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
I am trying to get selected mobile phone number with
ABMultiValueRef phones = ABRecordCopyValue(person, property);
CFStringRef phoneNumber = ABMultiValueCopyValueAtIndex(phones, identifier);
I have a contact with several mobile phones (all labeled 'mobile'). When I select the first one, phoneNumber gives me the first one, but if I select any consecutive one, phoneNumber gives me the previous number:
Contact:
Jay Jaymes
mobile +1111111111
mobile +2222222222
mobile +3333333333
Tap first one, phoneNumber = +1111111111
Tap second one, phoneNumber = +1111111111
Tap third one, phoneNumber = +2222222222
This is code I use. And It will give correct phone number only
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
if (property == kABPersonPhoneProperty) {
ABMultiValueRef phoneProperty = ABRecordCopyValue(person,property);
CFIndex peopleIndex = ABMultiValueGetIndexForIdentifier(property, identifier);
NSString *phone = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(phoneProperty, peopleIndex);
[self dismissModalViewControllerAnimated:YES];
}
return NO;
}
You can easily iterate like that. Try NSLoging that code to make sure it works.
I think you have some bugs in your "choosing" logic.
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
for (CFIndex i=0; i < ABMultiValueGetCount(phones); i++)
{
NSString* phoneLabel = (NSString*)ABMultiValueCopyLabelAtIndex(phones, i);
NSString* phoneNumber = ABMultiValueCopyValueAtIndex(phones, i);
//release variables since you were using COPY !!!
CFRelease(phoneNumber);
CFRelease(phoneLabel);
}
CFRelease(phones);
Ended up implementing it this way:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
if (property == kABPersonPhoneProperty)
{
ABMultiValueRef phones = ABRecordCopyValue(person, property);
CFStringRef phoneNumber = ABMultiValueCopyValueAtIndex(phones, identifier);
NSLog(#"%#", phoneNumber);
NSMutableString *tmp = [NSMutableString stringWithFormat:#"%#", (__bridge_transfer NSString *)phoneNumber];
NSString *strippedPhoneNumber = [tmp stringByReplacingOccurrencesOfString:#" " withString:#""];
NSCharacterSet *doNotWant = [NSCharacterSet characterSetWithCharactersInString:#"()-"];
strippedPhoneNumber = [[strippedPhoneNumber componentsSeparatedByCharactersInSet: doNotWant] componentsJoinedByString: #""];
NSString *firstName = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
[peoplePicker dismissViewControllerAnimated:YES completion:nil];
return NO;
}
You should not be using the identifier as the index within ABMultiValueCopyValueAtIndex. You should call ABMultiValueGetIndexForIdentifier to convert the ABMultiValueIdentifier identifier into a CFIndex index:
ABMultiValueRef phones = ABRecordCopyValue(person, property);
CFIndex index = ABMultiValueGetIndexForIdentifier(phones, identifier);
NSString *phoneNumber = CFBridgingRelease(ABMultiValueCopyValueAtIndex(phones, index));
CFRelease(phones);
Generally, the ABMultiValueIdentifier values match the CFIndex values retrieved by ABMultiValueGetIndexForIdentifier, but if a contact was edited (specifically if one of the earlier phone numbers was deleted), using the ABMultiValueIdentifier in ABMultiValueCopyValueAtIndex will return the wrong record.
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;