I want to display a ABAddressBook that only shows contacts with an email, so I tried something like this:
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople( addressBook );
CFIndex nPeople = ABAddressBookGetPersonCount( addressBook );
for( CFIndex emailIndex = 0; emailIndex < nPeople; emailIndex++ ) {
ABRecordRef person = CFArrayGetValueAtIndex( allPeople, emailIndex );
ABMutableMultiValueRef emailRef=ABRecordCopyValue(person, kABPersonEmailProperty);
int emailCount = ABMultiValueGetCount(emailRef);
if(emailCount == 0) {
ABAddressBookRemoveRecord(addressBook, person, NULL);
}
}
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.addressBook = addressBook;
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
The controller shows, but all the contacts are shown, and if I select the ones without an email, I get a crash. If I called ABAddressBookSave(), then it removes all contacts with an email, but it is a permanent change which even deletes them from the system contacts. What's the right way to do this?
You might need to build an array of contacts that have email, then display it on UITableViewController. This is a way to build such array: https://stackoverflow.com/a/13980023/745862. You can use ABPersonViewController or ABUnknownPersonViewController to display contact details.
Have you considered creating a temporary AddressBook and filling it with those contacts that have email addresses?
You remove all contacts without email address from AddressBook, but didn't save the AddressBook at the end. When the AddressBook is loaded from ABPeoplePickerNavigationController, all previous changes will be lost!
Try this:
if (ABAddressBookHasUnsavedChanges(addressBook))
ABAddressBookSave(addressBook, NULL)
BTW, don't forget to CFRelease() addressBook, allPeople and emailRef.
Did you try to show email values only like this:
ABPeoplePickerNavigationController *picker = [[ABPeoplePickerNavigationController alloc] init];
picker.displayedProperties = #[#(kABPersonEmailProperty)];
Perhaps that already does, what you want? Didn't test it though.
From iOS 8 the ABPeoplePickerNavigationController has a property that resolve your problem:
#property (nonatomic,copy) predicateForEnablingPerson
Code example from Apple doc: https://developer.apple.com/library/prerelease/ios/samplecode/PeoplePicker/Listings/PeoplePicker_AAPL_8or7_EmailPickerViewController_m.html)
ABPeoplePickerNavigationController* abPicker = [ABPeoplePickerNavigationController new];
abPicker.peoplePickerDelegate = self;
//...
if ([abPicker respondsToSelector:#selector(setPredicateForEnablingPerson:)]){
// The people picker will enable selection of persons that have at least one email address.
abPicker.predicateForEnablingPerson = [NSPredicate predicateWithFormat:#"emailAddresses.#count > 0"];
}
Note that from iOS9 the ABPeoplePickerNavigationController is deprecated, you can use the new CNContactPickerViewController in the same way with the property:
#property(nonatomic, copy) NSPredicate *predicateForEnablingContact
From the doc:
You can set a value for this property to determine which contact should become selectable, such as emailAddresses.#count > 0 to enable all the contacts that have an email address to become selectable. If no predicate is set for this property, all contacts become selectable.
Related
I have an array of dictionary for contact details. I am trying to add that record in ABRecordRef, but I don't understand how it works. Here is my code:
for (int i = 0; i <= [contactArray count]; i++)
{
ABRecordRef person = (ABRecordRef)[contactArray objectAtIndex:i];
ABAddressBookAddRecord(addressBook, group, &error);
ABAddressBookSave(addressBook, &error);
}
I am trying to add this contact records into group using ABGroupAddMember. Now how can I get the records from NSMutableArray. Any help will be greatly appreciated. Thank you.
there is no built-in functionality for that. you will have to create an empty record, THEN get all the fields from the dict and THEN add those to the record and safe it
that's annoying manual work... :D
I'd go with Erica's ABContactHelper: https://github.com/erica/ABContactHelper
then it is only
for(NSDictionary *d in recordArray) {
ABContact *contact = [ABContact contactWithDictionary:d];
ABGroupAddMember(theGroup, contact.record);
}
if you like manual:
for(NSDictionary *d in recordArray) {
ABRecordRef person = ABPersonCreate();
for(NSString *k in d.allKeys) {
id v = d[k];
//HERE call ABRecordSetValue with the right params depending on if the value is a NSString or NSArray or an image
}
ABGroupAddMember(theGroup, contact.record);
CFRelease(person);
}
disclaimer: typed inline but should be ok
What I trying to do, is get all contacts and relies if the contact have SocialProfileProperty or not, after get all this contacts that have SocialProfileProperty I will make simple filtration to know if it is Facebook,Twitter,..
Before I post this question I west full day trying to find any solution for this problem ??
But it is allows return 0 ??
ABAddressBookRef addressBook;
CFErrorRef *erer = NULL;
addressBook = ABAddressBookCreateWithOptions(NULL, erer);
CFArrayRef _allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex _nPeople = ABAddressBookGetPersonCount(addressBook);
NSMutableArray *socialArray = [[NSMutableArray alloc] init];
ABMultiValueRef socialProfiles;
for (int i=0;i<_nPeople; i++) {
ABRecordRef _person=CFArrayGetValueAtIndex( _allPeople,i);
NSNumber *recordId = [NSNumber numberWithInteger:ABRecordGetRecordID(_person)];
NSLog(#"%#",recordId);
socialProfiles = ABRecordCopyValue(_person, kABPersonSocialProfileProperty);
if (ABMultiValueGetCount(socialProfiles)>0) {
[socialArray addObject:(__bridge id)(socialProfiles)];
}
}
NSLog(#"%d",socialArray.count);
It is allows return 0 ,until the user marge the Social contacts with native and this does
not happened until he try to delete the Facebook account for example in that case alert appear
and ask if u wanna to marge contacts or not ??? other wise return 0 values :)
So this method will never work until the user marge the FaceBook Contacts with native contacts.
I'm rolling my own PeoplePicker, using the following code to get all my contacts:
ABAddressBookRef ab = ABAddressBookCreate();
ABRecordRef source = ABAddressBookCopyDefaultSource(ab);
NSArray *allContacts = (NSArray *) ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(ab, source, ABPersonGetSortOrdering());
It's worked fine during development, but now we're in beta, and a user complained that when they went to choose a contact in my app, the list they were given to choose from was a small subset of all their contacts in their phone.
We pinned it down to the fact that it was actually just displaying one of their groups.
A while later, I realized you can set your default group in Settings > Mail, Contacts, Calendars > Contacts > Default Group
If a user has a specific group selected, then just that group displays in my app.
I think this is because I'm using ABAddressBookCopyDefaultSource to get all the contacts.
How can I grab all a user's contacts without regard to the default source?
Thanks!
try this,
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for( int i=0;i< nPeople;i++)
{
ABRecordRef ref = CFArrayGetValueAtIndex(allPeople, i);
CFStringRef firstName = ABRecordCopyValue(ref, kABPersonFirstNameProperty);
.....
and so on
}
I have an iPhone app that uses a ABPeoplePickerNavigationController to pick out a contact. First time you select a contact, the contact list obviously starts at the top, under the letter 'A'. However, say you selected a contact under 'M'; if you then later want to change which contact is selected, I want to initialise the contact list so that it's already scrolled to the 'M' section.
Here's the code I use to open the contact list:
ABPeoplePickerNavigationController *picker =
[[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentModalViewController:picker animated:YES];
[picker release];
I can't find any information in the reference guide on how to configure the scroll offset where the controller opens.
This functionality is unavailable in ABPeoplePickerNavigationController. However you can implement a custom UITableViewController which does that using the AddressBook framework.
Try to use the [ABAddressBook]
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for( int i = 0 ; i < nPeople ; i++ )
{
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i );
NSString* name = [(NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty) autorelease];
}
I am trying to be an ABRecordRef that represents the contact info of a person from addressbook. I built two functions that calls a function to fill in a personal data structure with the info in ABRecordRef.
Here comes the function declarations for the three functions:
+ (NSMutableArray*) getAllContactProfiles{
NSMutableArray *listOfProfile = [[NSMutableArray alloc] init];
//---get the contact information for the api
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef people = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex numberOfPeopleInAddressBook = ABAddressBookGetPersonCount(addressBook);
//<- Here I loop through all the contacts and pass the ABRecordRef into the following function
//---release the variables---
CFRelease(addressBook);
CFRelease(people);
[listOfProfile autorelease];
return listOfProfile;
}
The Following Function
+ (MSProfileEntry*) getPersonProfileThroughABRecordRef:(ABRecordRef) person{
MSProfileEntry *mockProfile;
ABRecordID recID=ABRecordGetRecordID(person);
//get the user name
CFStringRef firstName;
firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);//it goes wrong here!
CFStringRef lastName;
lastName = ABRecordCopyValue(person, kABPersonLastNameProperty);
//bla bla bla.. the rest of the code
}
Everything goes very well. However, when I try to get the ABRecordRef through ABAddressBookGetPersonWithRecordID like it is in the next method:
The Next Method
+ (MSProfileEntry*) getPersonProfileThroughContactId:(NSInteger*)contactId{
ABAddressBookRef addressBook = ABAddressBookCreate();
ABRecordRef person =
ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID)contactId);
CFRelease(addressBook);
if (person == nil) {
return nil;
}
return [MSContactUtil getPersonProfileThroughABRecordRef:person];
}
The whole app crashes on line:ABRecordCopyValue(person, kABPersonFirstNameProperty);.
The problem now is that ABRecordCopyValue(person, kABPersonFirstNameProperty); works perfectly fine with ABAddressBookCopyArrayOfAllPeople but causes the app to crash with ABAddressBookGetPersonWithRecordID.
Does anyone have any clue how to solve this problem? I really don't want to loop through entire contact base just to look for a contact.
It turned out to be a memory issue. I forgot to retain the "addressBook". By the time I executed the following line:
firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty);
The "addressBook" had been cleaned out already. Somehow we still need "addressBook" while querying for the detail information in "person".
So, remember to put in the following line, and you will be safe.
CFRetain(addressBook);
Two things:
You pass (NSInteger*)contactId to getPersonProfileThroughContactId and after that you call ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID)contactId);. Actually you pass an address of the integer that holds the contact id and not the id itself...
You check if (person == nil), BUT person may not be nil - you should compare with NULL. I believe that it IS NULL in your case (because of my previous point).
These 2 things together cause the crash.
Just pass an integer as is - not its address...
EDIT:
Like this:
+ (MSProfileEntry*)getPersonProfileThroughContactId:(NSInteger)contactId