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

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.

Related

How do I get Facebook and custom social addresses from iOS address book?

In my peoplePickerNavigationController:shouldContinueAfterSelectingPerson I'm able to read a bunch of the iOS Contacts info from the chosen contact into my app.
The one item I can't figure out how to read is the Facebook (and other social) address info. Here's what I've got and I know it's not quite right. The problem has something to do with the label I think:
ABMultiValueRef socialRef = ABRecordCopyValue(person, kABPersonSocialProfileProperty);
for (int i = 0; i < ABMultiValueGetCount(socialRef); i++) {
CFStringRef currentSocialLabel = ABMultiValueCopyLabelAtIndex(socialRef, i);
CFStringRef currentSocialValue = ABMultiValueCopyValueAtIndex(socialRef, i);
if (CFStringCompare(currentSocialLabel, kABPersonSocialProfileServiceFacebook, 0) == kCFCompareEqualTo) {
[theContactInfoDictionary setObject:(__bridge NSString *)currentSocialValue forKey:#"theFacebook"];
}
CFRelease(currentSocialLabel);
CFRelease(currentSocialValue);
}
CFRelease(socialRef);
Please point me in the right direction.
Furthermore, I also need to check if there is a custom social entry for "Google+" (my app can create these entries in another place) and if there is, I want to import that value as well. How do I go about doing this?
Thanks!
sigh
And I figured it out. Here's what I did:
ABMultiValueRef socialRef = ABRecordCopyValue(person, kABPersonSocialProfileProperty);
for (int i = 0; i < ABMultiValueGetCount(socialRef); i++) {
NSDictionary *socialDictionary = (__bridge NSDictionary *)ABMultiValueCopyValueAtIndex(socialRef, i);
if ([socialDictionary[#"service"] isEqualToString:(__bridge NSString *)kABPersonSocialProfileServiceFacebook]) {
[theContactInfoDictionary setObject:(NSString *)socialDictionary[#"username"] forKey:#"theFacebook"];
} else if ([socialDictionary[#"service"] isEqualToString:(__bridge NSString *)kABPersonSocialProfileServiceTwitter]) {
[theContactInfoDictionary setObject:(NSString *)socialDictionary[#"username"] forKey:#"theTwitter"];
} else if ([socialDictionary[#"service"] isEqualToString:#"Google+"]) {
[theContactInfoDictionary setObject:(NSString *)socialDictionary[#"username"] forKey:#"theGoogle"];
}
}
CFRelease(socialRef);

Handling actions ABPersonViewController with linked contacts on iOS7

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);
}

kABPersonPhoneProperty Only Detecting Home Phone Numbers

I followed Apple's tutorial on obtaining a contact's phone number and it works great but it only detects phone numbers with the "Home" label. Here is my code:
- (void)displayPerson:(ABRecordRef)person {
NSString* phone = nil;
ABMultiValueRef phoneNumbers = ABRecordCopyValue(person,
kABPersonPhoneProperty);
if (ABMultiValueGetCount(phoneNumbers) > 0) {
phone = (__bridge_transfer NSString*)
ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
} else {
phone = #"[None]";
}
self.contactNumber.text = phone;
CFRelease(phoneNumbers); }
Anyone know why it is doing this?
That's because this line
ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
tells it to get the first phone number, i.e "Home" number.
See this post:
Access to people info in iPhone address book

iOS : Can't get the user email address

I want to get the user mail address, as shown in this thread : Getting user's default email address in Cocoa
But when I tried :
NSString *theEmailAddressWeWantToObtain = #"";
ABPerson *aPerson = [[ABAddressBook sharedAddressBook] me];
ABMultiValue *emails = [aPerson valueForProperty:kABEmailProperty];
if([emails count] > 0)
theEmailAddressWeWantToObtain = [emails valueAtIndex:0];
I have these errors :
Use of undeclared identifier 'aPerson'
Use of undeclared identifier 'ABAddressBook'
Unknown type 'ABMultiValue'
I've linked AddressBook and AddressBookUI, and imported AddressBook/AddressBook.h
What's wrong ?
These are the corrections to your code
NSString *theEmailAddressWeWantToObtain = #"";
ABPerson *aPerson = [[ABAddressBook sharedAddressBook] me];
ABMultiValueRef *emails = [aPerson valueForProperty:kABEmailProperty]; //No such thing as ABMultiValue; it's ABMultiValueRef
if(ABMultiValueGetCount(emails) > 0) //"emails" is not an array, so you can't use the "count" method
theEmailAddressWeWantToObtain = [emails valueAtIndex:0];
I'm not so familiar with Key Value Coding, so I'm not sure about your methods related to that.
This is the way I would do it
There are three email address stored in the email ABMultiValueRef: home, work, and other emails. Try this code to get the home email:
NSString *email;
ABRecordRef currentPerson = (__bridge ABRecordRef)[[PSAddressBook arrayOfContacts] objectAtIndex:identifier];
ABMultiValueRef emailsMultiValueRef = ABRecordCopyValue(currentPerson, kABPersonEmailProperty);
NSUInteger emailsCount;
//Goes through the emails to check which one is the home email
for(emailsCount = 0; emailsCount <= ABMultiValueGetCount(emailsMultiValueRef);emailsCount++){
NSString *emailLabel = (__bridge_transfer NSString *)ABMultiValueCopyLabelAtIndex (emailsMultiValueRef, emailsCount);
if([emailLabel isEqualToString:#"Home"]){
if ((__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(emailsMultiValueRef, emailsCount) != NULL){
email = (__bridge_transfer NSString *)ABRecordCopyValue(currentPerson, kABPersonEmailProperty);
}
//If the last name property does not exist
else{
email = #"NULL";
}
}
}
CFRelease(emailsMultiValueRef);
If you have any questions about the code, just ask in the comments. Hope this helps!
EDIT:
The PSAddressBook class mentioned in the code can be found here: https://github.com/pasawaya/PSAddressBook

AddressBook ABPerson Twitter Social Profile

Have you ever tried to get data fields from the address book and ran into the wonderful world of ABPerson reference only to find it reads like the blueprints to a space rocket?
I've gotten so far but I still need help getting just the Twitter username key & value:
//I tried this but I can't seem to get the if statement to work
ABMutableMultiValueRef socialMulti = ABRecordCopyValue(person, kABPersonSocialProfileProperty);
NSMutableDictionary *mySocialDict = [NSMutableDictionary dictionaryWithCapacity:ABMultiValueGetCount(socialMulti)];
NSLog(#"entering social dict of count %ld", ABMultiValueGetCount(socialMulti));
for (CFIndex i = 0; i < ABMultiValueGetCount(socialMulti); i++) { 
CFStringRef socialLabel = ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(socialMulti, i));
CFStringRef social = ABMultiValueCopyValueAtIndex(socialMulti, i); 
if ([(__bridge NSString*)socialLabel isEqualToString:#"twitter"]) {
NSLog(#"we got a twitter");
}
[mySocialDict setObject:(__bridge NSString*)social forKey:(__bridge NSString*)socialLabel];
NSLog(#"social is %#",social);
CFRelease(social);
CFRelease(socialLabel);
}
 
I'm actually just interested in the twitter username. I know I could get it from the dictionary I created but I want to get it directly. I plan to eliminate the NSDictionary step anyway.
Here's an excerpt from my code. Replace your var names as necessary.
ABMultiValueRef socialMulti = ABRecordCopyValue(recordRef, kABPersonSocialProfileProperty);
NSMutableArray* twitterHandlesArray = [[NSMutableArray alloc] initWithCapacity:ABMultiValueGetCount(socialMulti)];
for (CFIndex i = 0; i < ABMultiValueGetCount(socialMulti); i++) {
NSDictionary* social = (__bridge NSDictionary*)ABMultiValueCopyValueAtIndex(socialMulti, i);
if ([social[#"service"] isEqualToString:(__bridge NSString*)kABPersonSocialProfileServiceTwitter]) {
NSString* username = (NSString*)social[#"username"];
NSLog(#"we got a twitter. username is %#", username);
[twitterHandlesArray addObject:[[username conditionedAsTwitterHandle] SHA2Digest]];
}
}

Resources