Handling actions ABPersonViewController with linked contacts on iOS7 - ios

I am having an issue handling an action on an ABPersonViewController when the view controller is displaying a merged contact in iOS 7.
In the below example the ABPersonViewController showed 7 rows for the contact being displayed but when I fetch the ABMultiValueRef for the property it returns 8 rows where the first row was hidden because it was a duplicate on this combined contact.
When the value is read for the index it is the one that is hidden so all values read are one off the one that was displayed in the ABPersonViewController
-(BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
if (property == kABPersonPhoneProperty){
ABMultiValueRef phone = ABRecordCopyValue(person, property);
CFIndex theindex = ABMultiValueGetIndexForIdentifier(phone, identifier);
NSString *selectedValue = (NSString *)ABMultiValueCopyValueAtIndex(phone, the index);
// DO SOMETHING WITH THE SELECTED VALUE
CFSafeRelease(phone);
CFSafeRelease(selectedValue);
}
return NO;
}
Is there a way to get the correct value for the index when the contacts are combined, or is there a way to get the MultiValueRef for the property that was actually displayed in the ABPersonViewController?

Try This :
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person {
ABMultiValueRef phoneNumberProperty = ABRecordCopyValue(person, kABPersonPhoneProperty);
phoneNumbers = (__bridge NSArray*)ABMultiValueCopyArrayOfAllValues(phoneNumberProperty);
NSLog(#"%#",phoneNumbers);
CFRelease(phoneNumberProperty);
return NO;
}

ABPropertyType pt = ABPersonGetTypeOfProperty(property);
NSString *phoneNumber;
if ((pt & kABMultiValueMask) == kABMultiValueMask) {
ABMultiValueRef phoneProperty = ABRecordCopyValue(person,property);
CFIndex idx = ABMultiValueGetIndexForIdentifier(phoneProperty, identifier);
phoneNumber = (__bridge NSString *)ABMultiValueCopyValueAtIndex(phoneProperty,idx);
CFRelease(phoneProperty);
}

Related

How to check if a contact in Address Book has multiple numbers in iOS?

I have a requirement to determine if a contact that is saved in the address book has multiple mobile numbers.
I have the record ID of a Contact and I need to check if that contact has multiple mobile numbers.
Is this possible?
Got the Answer!!! I have written a method to get the total count of mobile numbers for a contact
-(NSInteger)getCountOfTotalMobileNumbersForContact:(ContactData *)contactData{
NSInteger totalCountOfMobileNumbers = 0;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
ABRecordRef contact = ABAddressBookGetPersonWithRecordID(addressBook, (int)contactData.mRecordID);
if(contact != nil)
{
self.phoneNumbers = ABRecordCopyValue(contact, kABPersonPhoneProperty);
NSString *lFirstName = (__bridge_transfer NSString*)ABRecordCopyValue(contact, kABPersonFirstNameProperty);
NSString *lLastName = (__bridge_transfer NSString*)ABRecordCopyValue(contact, kABPersonLastNameProperty);
NSString *fullName = lFirstName;
if (lLastName.length) {
fullName = [[lFirstName stringByAppendingString:#" "] stringByAppendingString:lLastName];
}
totalCountOfMobileNumbers = ABMultiValueGetCount(self.phoneNumbers);
}
return totalCountOfMobileNumbers;
}

find ios ABRecordRef contact in AddressBook by phone, email, name

I need to find a contact in AdreesBook in order to add a new social network.
Sometimes I have to find a contact by its phones and emails or by its phones, firstName and lastName, is there any kind of query to get ABRecordRef contact instead of doing whiles?
Many of my users have more than 1000 contacts and I need to update many of them, so it is not efficient if my only solution is to do so many whiles...
Any idea??
Thanks!
Below is method which may help you to get the Contact details using phone number. For that its used kABPersonPhoneProperty, same way you can write another functions for searching email and name:
For email, use a property : kABPersonEmailProperty
For First Name : kABPersonFirstNameProperty
For more details, go through: ABPerson Reference
Hope this helps.
#import <AddressBook/AddressBook.h>
-(NSArray *)contactsContainingPhoneNumber:(NSString *)phoneNumber {
/*
Returns an array of contacts that contain the phone number
*/
// Remove non numeric characters from the phone number
phoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:#""];
// Create a new address book object with data from the Address Book database
CFErrorRef error = nil;
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
if (!addressBook) {
return [NSArray array];
} else if (error) {
CFRelease(addressBook);
return [NSArray array];
}
// Requests access to address book data from the user
ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {});
// Build a predicate that searches for contacts that contain the phone number
NSPredicate *predicate = [NSPredicate predicateWithBlock: ^(id record, NSDictionary *bindings) {
ABMultiValueRef phoneNumbers = ABRecordCopyValue( (__bridge ABRecordRef)record, kABPersonPhoneProperty);
BOOL result = NO;
for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
NSString *contactPhoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
contactPhoneNumber = [[contactPhoneNumber componentsSeparatedByCharactersInSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]] componentsJoinedByString:#""];
if ([contactPhoneNumber rangeOfString:phoneNumber].location != NSNotFound) {
result = YES;
break;
}
}
CFRelease(phoneNumbers);
return result;
}];
// Search the users contacts for contacts that contain the phone number
NSArray *allPeople = (NSArray *)CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(addressBook));
NSArray *filteredContacts = [allPeople filteredArrayUsingPredicate:predicate];
CFRelease(addressBook);
return filteredContacts;
}

How do you retrieve ONLY the select property from your iOS contacts?

we're having an issue where incorrect selections are being returned from the contacts database on some devices. Is there a way to modify the below code to only return the selected item from a persons contact information? In this case we want the exact selected email address from a persons contact to be returned.
Thanks in advance for the help! Heres the code...
-(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property: (ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
NSMutableDictionary *contactInfoDict = [[NSMutableDictionary alloc]
initWithObjects:#[#"", #"", #""]
forKeys:#[#"first_name", #"last_name", #"email"]];
// Use a general Core Foundation object.
CFTypeRef generalCFObject = ABRecordCopyValue(person, kABPersonFirstNameProperty);
// Get the first name.
if (generalCFObject) {
[contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:#"first_name"];
CFRelease(generalCFObject);
}
// Get the last name.
generalCFObject = ABRecordCopyValue(person, kABPersonLastNameProperty);
if (generalCFObject) {
[contactInfoDict setObject:(__bridge NSString *)generalCFObject forKey:#"last_name"];
CFRelease(generalCFObject);
}
// Load selected email addre'
if (property == kABPersonEmailProperty) {
ABMultiValueRef emails = ABRecordCopyValue(person, property);
CFIndex ix = ABMultiValueGetIndexForIdentifier(emails, identifier);
CFStringRef email = ABMultiValueCopyValueAtIndex(emails, ix);
emailAccount = (__bridge NSString *)(email);
[contactInfoDict setObject:emailAccount forKey:#"email"];
if (email) CFRelease(email);
if (emails) CFRelease(emails);
}
// init array
if (_arrContactsData == nil) {
_arrContactsData = [[NSMutableArray alloc] init];
}
// Send contact to server
[self sendContact:contactInfoDict];
// Set data for contacts
[_arrContactsData addObject:contactInfoDict];
// Reload the table view data.
[self.myTable reloadData];
// Dismiss the address book view controller.
[_addressBookController dismissViewControllerAnimated:YES completion:nil];
return NO;
}
Your code is correct.
Are the users experiencing the problem using iOS 8, by any chance? I discovered a bug in iOS 8 beta 6. If you:
Create a contact with email addresses:
Edit the contact, adding an email address and deleting one of the other email addresses:
(Note, don't just edit one of the email addresses, but rather add a new email address and remove and old one.)
In iOS 7, if you iterate through the email addresses, you will see:
{
identifier = 1;
index = 0;
label = "_$!<Work>!$_";
value = "work#gmail.com";
},
{
identifier = 2;
index = 1;
label = "_$!<Other>!$_";
value = "other#gmail.com";
}
This is correct. (When the contact was first created, the identifiers were 0 and 1, when I add the "other" email address, that gets an identifier of 2; when I delete the "home" email address, the email address with the identifier of 0 is deleted, but the other identifiers are correctly unaltered.) Thus, if you tap on the "work" email address, shouldContinueAfterSelectingPerson will return an ABMultiValueIdentifier of 1, which translates to a CFIndex of 0, which will correctly return the "work" email address.
In iOS 8 Beta 6, however, the ABMultiValueRef is incorrectly represented as:
{
identifier = 0;
index = 0;
label = "_$!<Work>!$_";
value = "work#gmail.com";
},
{
identifier = 1;
index = 1;
label = "_$!<Other>!$_";
value = "other#gmail.com";
}
These identifiers are incorrect. Worse, if you tap on the "work" email address, didSelectPerson (which replaces the deprecated shouldContinueAfterSelectingPerson) will return a ABMultiValueIdentifier of 1 (which is what it should have been ... it looks like this delegate method returns the appropriate identifier, but that the ABRecordRef for this edited record has the wrong identifiers ... not sure how they did that!), which translates to a CFIndex of 1, which will correctly return the "other" email address (!).
Worse, if you tapped on the "other" email address, didSelectPerson will return a ABMultiValueIdentifier of 2 (which is what it should have been), but if you try to retrieve the CFIndex, you'll get -1 (because no such ABMultiValueIdentifier exists) and because the app doesn't check for this, any attempt to use ABMultiValueCopyValueAtIndex with this invalid index will result in crash!
This bug seems to be fixed in the iOS8 GM seed, so it appears to be a beta problem only.

Check if ABMultiValueRef is has no values

I want to check if a contact in my user's addressbook a phone number has. If he does, I want to display that name in an UITableView
I've tried to check for phoneNumbers != nil, but that doesn't work. This is my entire code:
ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
if(phoneNumbers != nil){
[_numbers addObject:[NSString stringWithFormat:#"%#", phoneNumbers]];
}
Use ABMultiValueGetCount to check if phoneNumbers has any values in it.
example based on question:
ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
if(ABMultiValueGetCount(phoneNumbers)){
[_numbers addObject:[NSString stringWithFormat:#"%#", phoneNumbers]];
}

Contact number stays nil

I have an mutableArray and put the number of a contact in it.
I use the following code:
ABMultiValueRef phoneNumbers = (ABMultiValueRef)ABRecordCopyValue(person, kABPersonPhoneProperty);
CFRelease(phoneNumbers);
number = (__bridge NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
[thenumbers addObject:number];
In order to retrieve it, I do this:
Contacts *num = [thenumbers objectAtIndex:indexPath.row];
NSString *numbers = [NSString stringWithFormat:#"%#", [num number]];
cell.detailTextLabel.text = numbers;
I set breakpoints and it stops at the right lines. I also tried
NSLog(#"%#", number);
And it returns the numbers. And yes I have reloadDate in viewWillappear.
You're releasing phoneNumbers straight after copying to it. Try moving it to after you use it.
Or, better still, transfer the ownership to the NSString object through ARC
ABMultiValueRef phoneNumbers = (ABMultiValueRef)ABRecordCopyValue(person, kABPersonPhoneProperty);
number = (__bridge_transfer NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
And there is no need to call CFRelease(phoneNumbers) at all.
As for getting the numbers into the cell, you're getting confused about your types. You're putting an NSString into theNumber but your pulling out a Contacts. And then sending it some kind of number message.
You've put a string in the array, you can only pull out a string or a subclass.
NSString *numbers = [thenumbers objectAtIndex:indexPath.row];
cell.detailTextLabel.text = numbers;
In the following code,
ABMultiValueRef phoneNumbers = (ABMultiValueRef)ABRecordCopyValue(person, kABPersonPhoneProperty);
CFRelease(phoneNumbers);
number = (__bridge NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
You are releasing the phoneNumbers and then you are accesing the variable. Change it to as follows.
ABMultiValueRef phoneNumbers = (ABMultiValueRef)ABRecordCopyValue(person, kABPersonPhoneProperty);
number = (__bridge NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
CFRelease(phoneNumbers);

Resources