I need to be able to read the contacts' phone number from the user's address book. The problem is that if the user has chosen to sync these contacts via Facebook, they are no longer accessible via the following code (which does work for non-synced contacts):
ABMultiValueRef phones = ABRecordCopyValue(record, kABPersonPhoneProperty);
DLog(#"Found %ld phones", ABMultiValueGetCount(phones));
for(CFIndex j = 0; j < ABMultiValueGetCount(phones); j++)
{
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(phones, j);
CFStringRef locLabel = ABMultiValueCopyLabelAtIndex(phones, j);
NSString *phoneLabel =(__bridge NSString*) ABAddressBookCopyLocalizedLabel(locLabel);
NSString *phoneNumber = (__bridge NSString *)phoneNumberRef;
CFRelease(phoneNumberRef);
CFRelease(locLabel);
DLog(#" - %# (%#)", phoneNumber, phoneLabel);
[numbersArr addObject:phoneNumber];
}
The log result is [Line 126] Found 0 phones
I have tried to use CFArrayRef userNumbers = ABMultiValueCopyArrayOfAllValues(phoneNumbers);
, but this also returns nothing: [Line 118] Got user numbers: (null)
So I tried to dig into the social profiles, and this also returns nothing!
// Try to get phone numbers from social profile
ABMultiValueRef profiles = ABRecordCopyValue(record, kABPersonSocialProfileProperty);
CFIndex multiCount = ABMultiValueGetCount(profiles);
for (CFIndex i=0; i<multiCount; i++) {
NSDictionary* profile = (__bridge NSDictionary*)ABMultiValueCopyValueAtIndex(profiles, i);
NSLog(#"TESTING - Profile: %#", profile);
}
DLog(#"Got profiles: %#", profiles);
CFRelease(profiles);
Yet the log entry is:
[Line 161] Got profiles: ABMultiValueRef 0x1ddbb5c0 with 0 value(s)
If the above results are all not yielding me anything, how am I supposed to know they're a Facebook user & get their phone info?
From Apple Support:
We do not have any APIs that will return a unified Address Book
contact. However, you can retrieve phone numbers of contacts that
appear as unified in Contacts on your device by first using
ABPersonCopyArrayOfAllLinkedPeople to retrieve all linked contacts,
then iterating through these contacts to fetch their respective phone
numbers. Note that phone numbers that do not appear in the UI in
Contacts will not be returned by Address Book APIs. See below for a
snippet of code that will allow you to do so:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
//Fetch all linked contacts
CFArrayRef linkedPerson = ABPersonCopyArrayOfAllLinkedPeople(person);
//Iterate through each linked contact to fetch the phone numbers
for (CFIndex i = 0; i < CFArrayGetCount(linkedPerson); i++)
{
ABRecordRef contact = CFArrayGetValueAtIndex(linkedPerson,i);
ABMutableMultiValueRef multi = ABRecordCopyValue(contact, kABPersonPhoneProperty);
for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++)
{
CFStringRef label = ABMultiValueCopyLabelAtIndex(multi, i);
CFStringRef number = ABMultiValueCopyValueAtIndex(multi, i);
CFRelease(label);
CFRelease(number);
}
CFRelease(multi);
}
CFRelease(linkedPerson);
return YES;
Related
I am making an application which is compatible with iOS5 and it I want to take permission before to access the user contact list. In iOS6 I am able to do this but in iOS5 I have not found any code for to this. I am using it to access the contact list -
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef all = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex n = ABAddressBookGetPersonCount(addressBook);
for( int i = 0 ; i < n ; i++ )
{
ABRecordRef ref = CFArrayGetValueAtIndex(all, i);
ABMultiValueRef *phones = ABRecordCopyValue(ref, kABPersonPhoneProperty);
for(CFIndex j = 0; j < ABMultiValueGetCount(phones); j++)
{
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(phones, j);
//CFRelease(phones);
NSString *phoneNumber = (__bridge NSString *)phoneNumberRef;
CFRelease(phoneNumberRef);
// NSString *s = #"foo/bar:baz.foo";
NSCharacterSet *doNotWant = [NSCharacterSet characterSetWithCharactersInString:#"+- *#( )"];
phoneNumber = [[phoneNumber componentsSeparatedByCharactersInSet: doNotWant] componentsJoinedByString: #""];
NSLog(#"phone number new %#", phoneNumber);
}
}
Please suggest how to do this.
Since the Address Book permission requirement was only recently added as of iOS 6 you don't have to ask for permission.
If you still want to do that then you would simply show an UIAlertView and record the user's choice in NSUserDefaults.
Note though that if the user upgrades from iOS 5 to 6 he would be asked again, so you would have to disable your iOS 5 permission dialog in that case.
I am accessing all of the users contacts and emailing them through the app. I can access all of the contacts fine, if they have an email address. If they don't have an email address, then it gives me an error, I am trying to check if they have an email address before I attempt to add the address to the list of addresses. I am using this code. It gives me an error on the line of the if statement here: if ((ABMultiValueCopyValueAtIndex(email, 0) != NULL))
How can I access only the contacts who have email addresses?
NSMutableArray* contactsArray = [NSMutableArray new];
ABAddressBookRef m_addressbook = ABAddressBookCreate();
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(m_addressbook);
CFIndex nPeople = ABAddressBookGetPersonCount(m_addressbook);
for (int i=0;i < nPeople;i++)
{
NSMutableDictionary* tempContactDic = [NSMutableDictionary new];
ABRecordRef ref = CFArrayGetValueAtIndex(allPeople,i);
CFStringRef firstName, lastName;
firstName = ABRecordCopyValue(ref, kABPersonFirstNameProperty);
lastName = ABRecordCopyValue(ref, kABPersonLastNameProperty);
NSString *name = [NSString stringWithFormat:#"%# %#", firstName, lastName];
[tempContactDic setValue:name forKey:#"name"];
//fetch email id
NSString *strEmail;
ABMultiValueRef email = ABRecordCopyValue(ref, kABPersonEmailProperty);
if ((ABMultiValueCopyValueAtIndex(email, 0) != NULL))
{
CFStringRef tempEmailref = ABMultiValueCopyValueAtIndex(email, 0);
strEmail = (__bridge NSString *)tempEmailref;
[contactsArray addObject:strEmail];
}
}
The error is because you are trying to check whether there are email address available by copying the first item in the array and check if the value is NULL. But if the array is empty you can't copy any items from it.
What you should do is check if the array holds any items:
if (ABMultiValueGetCount(email) > 0) {
// There is at least one item in the email array/
}
Also be aware they your code only copies the first e-mail address for the contact, if more then 1 e-mail is set for that contact you might want to loop thru the e-mail array.
U can use this code to fetch all emails of all users from your contacts.
- (void) getEmailsOfAllContacts
{
ABAddressBookRef _addressBookRef = ABAddressBookCreate();
NSArray * allPeople = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(_addressBookRef);
for (id record in allPeople)
{
CFTypeRef emailProperty = ABRecordCopyValue((__bridge ABRecordRef)record, kABPersonEmailProperty);
NSArray * allEmailArray = ((__bridge NSArray *)ABMultiValueCopyArrayOfAllValues(emailProperty));
NSString * email;
for (int i = 0; i < [allEmailArray count]; i++)
{
email = [allEmailArray objectAtIndex: i];
if (isEmpty(email) == NO)
{
[_allContactsEmailArray addObject: email];
}
}
}
CFRelease(_addressBookRef);
allPeople = nil;
}
I am importing contacts info into some textfields, but the app crashes if there are no entries for certain fields, like phone, email, etc.
Here are my textfields:
First Name
Middle Name
Last Name
Main Phone
Mobile Phone
Email Address
Website
Suppose the selected contact doesnt have a second Phone number (in this case: Mobile Phone) or there are no URL entries for the contact. The app crashes.
*ABMutableMultiValueRef phoneMulti = ABRecordCopyValue(person, kABPersonPhoneProperty);
NSMutableArray *phones = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < ABMultiValueGetCount(phoneMulti); i++) {
NSString *aPhone = [(NSString*)ABMultiValueCopyValueAtIndex(phoneMulti, i) autorelease];
[phones addObject:aPhone];
}
accountPhone1TextField.text = [phones objectAtIndex:0];
accountPhone2TextField.text = [phones objectAtIndex:1];
CFRelease(phoneMulti);
[phones release];*
Or if I am trying to get an email address from the contact and it doesnt exist this will crash:
NSString *anEmail = [(NSString*)ABMultiValueCopyValueAtIndex(emailMulti, i) autorelease];
//Variable is not a CFString
***** UPDATE *****
Ended up using the following code:
ABMutableMultiValueRef emailMulti = ABRecordCopyValue(person, kABPersonEmailProperty);
NSMutableDictionary *myEmailDict = [NSMutableDictionary dictionaryWithCapacity:ABMultiValueGetCount(emailMulti)];
for (CFIndex i = 0; i < ABMultiValueGetCount(emailMulti); i++) {
emailLabel = ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(emailMulti, i));
email = ABMultiValueCopyValueAtIndex(emailMulti, i);
[myEmailDict setObject:(NSString*)email forKey:(NSString*)emailLabel];
CFRelease(email);
CFRelease(emailLabel);
}
if (phones.count > 0) {
accountPhone1TextField.text = [phones objectAtIndex:0];
}
if (phones.count > 1) {
accountPhone2TextField.text = [phones objectAtIndex:1];
}
All I want to do is let the user select a number from the address book. I found the code in this question:
How to get a Phone Number from an Address Book Contact (iphone sdk)
ABMultiValueRef container = ABRecordCopyValue(person, property);
CFStringRef contactData = ABMultiValueCopyValueAtIndex(container, identifier);
CFRelease(container);
NSString *contactString = [NSString stringWithString:(NSString *)contactData];
CFRelease(contactData);
The problem is that on the second line (when running on a 3.0 device) I get the following error:
Account Manager could not find account with identifier MobileMe:rustyshelf
followed by:
Program received signal: "EXC_BAD_ACCESS".
This is all inside the picker delegate method:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier{
This is just one of the contacts in my address book, which is synched with Mobile Me
Edit: I think this might be a bug with the SDK, it happens for some of my contacts but not for others...
The "identifier" argument does not hold the CFIndex of the record touched. The method I use to tell which phone number a user selected is this:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier
{
if (property == kABPersonPhoneProperty) {
ABMultiValueRef multiPhones = ABRecordCopyValue(person, kABPersonPhoneProperty);
for(CFIndex i = 0; i < ABMultiValueGetCount(multiPhones); i++) {
if(identifier == ABMultiValueGetIdentifierAtIndex (multiPhones, i)) {
CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(multiPhones, i);
CFRelease(multiPhones);
NSString *phoneNumber = (NSString *) phoneNumberRef;
CFRelease(phoneNumberRef);
txtPhoneNumber.text = [NSString stringWithFormat:#"%#", phoneNumber];
[phoneNumber release];
}
}
}
[self dismissModalViewControllerAnimated:YES];
return NO;
}
Going off of JWDs answer, here is a safer version that uses the builtin constants and chooses the iphone number over another mobile number...
ABMultiValueRef phones =(NSString*)ABRecordCopyValue(person, kABPersonPhoneProperty);
NSString* mobile=#"";
NSString* mobileLabel;
for(CFIndex i = 0; i < ABMultiValueGetCount(phones); i++) {
mobileLabel = (NSString*)ABMultiValueCopyLabelAtIndex(phones, i);
if([mobileLabel isEqualToString:(NSString *)kABPersonPhoneMobileLabel])
{
[mobile release] ;
mobile = (NSString*)ABMultiValueCopyValueAtIndex(phones, i);
}
else if ([mobileLabel isEqualToString:(NSString*)kABPersonPhoneIPhoneLabel])
{
[mobile release] ;
mobile = (NSString*)ABMultiValueCopyValueAtIndex(phones, i);
break ;
}
}
- (BOOL)personViewController:(ABPersonViewController *)personViewController shouldPerformDefaultActionForPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifierForValue
{
if (property == kABPersonPhoneProperty)
{
ABMultiValueRef numbers = ABRecordCopyValue(person, property);
NSString* targetNumber = (__bridge NSString *) ABMultiValueCopyValueAtIndex(numbers, ABMultiValueGetIndexForIdentifier(numbers, identifierForValue));
NSLog(#"%#", targetNumber);
}
return NO;
}
I used this question to construct my own solution. The code I'm posting is not intended as answer, merely for someone to maybe find useful. These are the simple steps for get a property. Memory management excluded.
I use this to pull the mobile number from an ABRecordRef/ The "record" variable is the ABRecordRef you want the phone number for. Where I have "" you can use another phone tag string to find other types of phone numbers.
//Get mobile phone number
ABMultiValueRef phones =(NSString*)ABRecordCopyValue(record, kABPersonPhoneProperty);
NSString* mobile=#"";
NSString* mobileLabel;
for(CFIndex i = 0; i < ABMultiValueGetCount(phones); i++) {
mobileLabel = (NSString*)ABMultiValueCopyLabelAtIndex(phones, i);
if([mobileLabel isEqualToString:#"_$!<Mobile>!$_"]) {
mobile = (NSString*)ABMultiValueCopyValueAtIndex(phones, i);
}
}
You should be able to just do the following:
ABMultiValueRef phoneNumbers = (ABMultiValueRef)ABRecordCopyValue(person, kABPersonPhoneProperty);
CFRelease(phoneNumbers);
NSString* phoneNumber = (NSString*)ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
Of course, that's only going to get you the first number of (potentially) many associated with the person who's been selected.
you can also use this "ABMultiValueGetIndexForIdentifier" like in this code :
ABPropertyType pt = ABPersonGetTypeOfProperty(property);
NSString *phoneNumber;
if ((pt & kABMultiValueMask) == kABMultiValueMask) {
ABMultiValueRef phoneProperty = ABRecordCopyValue(person,property);
CFIndex idx = ABMultiValueGetIndexForIdentifier(phoneProperty, identifier);
phoneNumber = (NSString *)ABMultiValueCopyValueAtIndex(phoneProperty,idx);
CFRelease(phoneProperty);
}
Going off from Dan's answer :
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
property:(ABPropertyID)property
identifier:(ABMultiValueIdentifier)identifier
{
if (property == kABPersonPhoneProperty) { // if tapped is equal to a phone property
CFStringRef cfnumber;
ABMultiValueRef numbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
for(CFIndex i = 0; i < ABMultiValueGetCount(numbers); i++) {
if(identifier == ABMultiValueGetIdentifierAtIndex (numbers, i)) { //if tapped number identifier is the same as identifier number tapped
cfnumber = ABMultiValueCopyValueAtIndex(numbers, i); // copy the number to CFSTRING number
}
}
NSString *number = [NSString stringWithFormat:#"%#",cfnumber];
CFRelease(cfnumber);
//do anything you want with the number
}
return NO;
}
Can't tell you if it's the best/correct way. But i ran into some errors using Dan's code.. hence i've decided to share it after figuring it out.
Still learning objective c.. haha..
Hope it helps..
Regards,
Steve0hh
I need to get the user emails those are configured in iPhone using cocoa touch. How can i do that .
Thank you for answers .
This code will fetch all contacts in the address book and output the email addresses associated with them:
// get a list of all contacts in the address book db
ABAddressBookRef ab = ABAddressBookCreate();
CFArrayRef contacts = ABAddressBookCopyArrayOfAllPeople(ab);
CFIndex nPeople = ABAddressBookGetPersonCount(ab);
for( int i = 0; i < nPeople; ++i ) {
ABRecordRef ref = CFArrayGetValueAtIndex(contacts,i);
// output the contact's name
CFStringRef firstName = ABRecordCopyValue(ref, kABPersonFirstNameProperty);
CFStringRef lastName = ABRecordCopyValue(ref, kABPersonLastNameProperty);
NSString *contactFirstLast = [NSString stringWithFormat:#"%#,%#:",
firstName, lastName];
NSLog(#"%#", contactFirstLast);
// output all email addresses stored for the contact
ABMultiValueRef emailRef = ABRecordCopyValue(ref, kABPersonEmailProperty);
CFIndex nEmails = ABMultiValueGetCount(emailRef);
CFArrayRef emails = ABMultiValueCopyArrayOfAllValues(emailRef);
for( int j = 0; j < nEmails; ++j ) {
CFStringRef email = CFArrayGetValueAtIndex(emails, j);
NSLog(#"\t%#", email);
};
// clean up
CFRelease(emailRef);
CFRelease(firstName);
CFRelease(lastName);
}
CFRelease(contacts);