Does anyone have an example of how to obtain a specific ABSource from the ABAddressBook in iOS 4+?
iOS 4+ provides new API that allows one to select a specific ABSource from the ABAddressBook. This may be useful as some operations, e.g. creating an ABGroup, are not supported in some sources (i.e. Exchange).
"Not all source types support groups, more conspicuously, Exchange does not know anything about groups." - http://flavors.me/volonbolon#1a5/tumblr
Attached are functions that leverage the new API to obtain sources of specific types which may be used in calls to ABGroupCreateInSource().
#define CFRELEASE_AND_NIL(x) CFRelease(x); x=nil;
ABRecordRef sourceWithType (ABSourceType mySourceType)
{
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef sources = ABAddressBookCopyArrayOfAllSources(addressBook);
CFIndex sourceCount = CFArrayGetCount(sources);
ABRecordRef resultSource = NULL;
for (CFIndex i = 0 ; i < sourceCount; i++) {
ABRecordRef currentSource = CFArrayGetValueAtIndex(sources, i);
CFTypeRef sourceType = ABRecordCopyValue(currentSource, kABSourceTypeProperty);
BOOL isMatch = mySourceType == [(NSNumber *)sourceType intValue];
CFRELEASE_AND_NIL(sourceType);
if (isMatch) {
resultSource = currentSource;
break;
}
}
CFRELEASE_AND_NIL(addressBook);
CFRELEASE_AND_NIL(sources);
return resultSource;
}
ABRecordRef localSource()
{
return sourceWithType(kABSourceTypeLocal);
}
ABRecordRef exchangeSource()
{
return sourceWithType(kABSourceTypeExchange);
}
ABRecordRef mobileMeSource()
{
return sourceWithType(kABSourceTypeMobileMe);
}
Really wanna know how to create my own source.Just like the group Exchange create with which you dont need to edit the default source record but create own one,and what's most fantastic is,the addressbook will linked them together automatically.
Xyzzycoder-
Your solution works well if there is already a localSource, but just returns NULL if there isn't one.
Is there a way to, say, create an ABRecordRef for a localSource? I need to be able to store my contact to a non-synchronising source.
Cheers
The code has errors, thats why it always returns two, since the method: ABRecordGetRecordType is not a part of the ABSource. It only includes:
kABPersonType for person records
kABGroupType for group records.
kABSourceType for source records.
To figure out the right type you have to use: ABRecordCopyValue(source, kABSourceTypeProperty) instead! :) Works excellent on my iPhone with or without localSource.
Good luck!
Related
Hi I have several Groups in my iPhone addressbook which contains several contacts. For Example:
iPhone addressbook, Group1, Group2 etc.
Each group contains contacts information like First Name,Last name,Email ,Phone number. Now by selecting any group I should get all details of added contacts in it. Can anybody please guide me how can I get all contacts details from particular group?
Please need some guidelines.
CFErrorRef error = NULL;
ABAddressBookRef addrBook = ABAddressBookCreate();
CFArrayRef groups = ABAddressBookCopyArrayOfAllGroups(addrBook);
CFIndex numGroups = CFArrayGetCount(groups);
for(CFIndex idx=0; idx<numGroups; ++idx) {
ABRecordRef groupItem = CFArrayGetValueAtIndex(groups, idx);
CFArrayRef members = ABGroupCopyArrayOfAllMembers(groupRef);
if(members) {
NSUInteger count = CFArrayGetCount(members);
for(NSUInteger idx=0; idx<count; ++idx) {
ABRecordRef person = CFArrayGetValueAtIndex(members, idx);
// your code
}
CFRelease(members);
}
}
CFRelease(groups);
CFRelease(addrBook);
This code is not guaranteed leak-proof so double check it. Its more or less correct.
Everything is explained in the documentation, so please tell use what you don't understand in it. What did you try? What did you get, which errors did you have?
If you want to work with contacts, in addition to the very complete Address Book Programming Guide, you have of course the Address Book Framework Reference and especially the ABGroup Reference Documentation to work with groups. And the latter contains an explicitly a method to get all members of a group. So you should have everything you need here.
CFArrayRef cfmembers = ABGroupCopyArrayOfAllMembers(group);
NSArray* members = (NSArray*)cfmembers; // working with NSArray is usually easier that CFArrays so I like using toll-free bridging
for(ABRecordRef person in members)
{
// ... your code ...
}
CFBridgingRelease(cfmembers); // release memory when done, following the usual memory mgmt rules
I have an app which crashes occasionally due to the array returned by ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering() having a different size to ABAddressBookGetPersonCount(). The shell of the code is shown below. Usually nPeople is the same size as the array. However, on one user's iPhone (or at least, as reported by one user), nPeople is almost twice as large. I can stop the crash by using the array size, rather than ABAddressBookGetPersonCount(). However, I am not sure if this means I am not accessing all of the Contacts in the iPhone.
Has anyone come across this issue before?
Why would the numbers be different?
I wondered if it was something to do with the contacts being stored in Groups (I do not know that there are groups - just an idea). Also, from the user's email address, I suspect they use MobileMe. I wondered if syncing with MobileMe would create duplicates with a different recordId, but not delete the existing Contact, at least not as far as ABAddressBookGetPersonCount() goes.
EDIT:
I have looked into this some more and have a fairly good idea at the cause of the problem. As I wanted a sorted array of contacts, I used ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(). This requires an address book source - I used the default source. I believe there can be various sources - the local source plus others such as Exchange and MobileMe. Therefore, my array will end up with just the local contacts, whereas the number returned by ABAddressBookGetPersonCount() will include all sources - hence my crash. Therefore, I think it would be better to just work with the local data in my app and use the array size returned by ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering rather than ABAddressBookGetPersonCount.
CFArrayRef allPeople = InSourceWithSortOrdering(addressBook, source, kABPersonSortByLastName);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for (int i = 0; i < nPeople; i++)
{
ABRecordRef ref = CFArrayGetValueAtIndex(allPeople, i);
}
ABAddressBookGetPersonCount And ABAddressBookCopyArrayOfAllPeople gives different arrays.
ABAddressBookGetPersonCount - Returns the number of person records in an address book.
ABAddressBookCopyArrayOfAllPeople - Returns all the person records in an address book.
So some times same person may have extra records. That's why you may getting different sizes.
I meet this problem today. My app also crashes in some special iDevices.
code:
ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByLastName);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for (int i = 0; i < nPeople; i++) {
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
// more thing with `person`
}
But it will crash sometimes. Adding some breakpoints I found that allPeople's count is less than nPeople.
By googling, I found this article. I found that maybe there's something wrong with ABAddressBookCopyDefaultSource. Here I got the default source, I have to got all sources instead.
code:
CFArrayRef sources = ABAddressBookCopyArrayOfAllSources(addressBook);
CFIndex sourceCount = CFArrayGetCount(sources);
for (CFIndex i = 0; i < sourceCount; i++) {
ABRecordRef currentSource = CFArrayGetValueAtIndex(source, i);
int sourceType = [(__bridge NSNumber *)ABRecordCopyValue(currentSource, kABSourceTypeProperty) intValue];
switch (sourceType) {
case kABSourceTypeCardDAV:
NSLog(#"kABSourceTypeCardDAV");
break;
case kABSourceTypeCardDAVSearch:
NSLog(#"kABSourceTypeCardDAVSearch");
break;
case kABSourceTypeExchange:
NSLog(#"kABSourceTypeExchange");
break;
case kABSourceTypeExchangeGAL:
NSLog(#"kABSourceTypeExchangeGAL");
break;
case kABSourceTypeLDAP:
NSLog(#"kABSourceTypeLDAP");
break;
case kABSourceTypeLocal:
NSLog(#"kABSourceTypeLocal");
break;
case kABSourceTypeMobileMe:
NSLog(#"kABSourceTypeMobileMe");
break;
default:
break;
}
CFArrayRef allPeopleInSource = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, currentSource, kABPersonSortByLastName);
NSLog(#"Count of allPeopleInSource: %i", CFArrayGetCount(allPeopleInSource));
}
Then I got
kABSourceTypeCardDAV
Count of allPeopleInSource: 7
which means there is only one source and only 7 records in that source.
But in my address book, I have 79 contacts!
Then I made a mistake. I passed the sources to ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering, just like this:
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, sources, kABPersonSortByLastName);
How many members in allPeople?
72!!!
Exactly the count of records which are not in sources.
I passed a CFArrayRef to ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering as the second parameter, which expects a ABRecordRef. What if I pass nil?
Finally, I got the codes:
CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, nil, kABPersonSortByLastName);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for (int i = 0; i < nPeople; i++) {
ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);
// more thing with `person`
}
Now I can get all of the contacts in my address book.
It works fine on all of my iDevices!
I'm very sorry about my poor english...
Hopefully, this answer can help you.
Note: Now I don't understand what is source in AddressBook exactly, Can anybody help me?
#Jokinryou Tsui Passing source as nil was the key, thanks! It seems like the source types may correspond to this documentation from Apple :
https://developer.apple.com/library/ios/documentation/AddressBook/Reference/ABSourceRef_iPhoneOS/#//apple_ref/doc/constant_group/Source_Types
Some users have multiple address books in their iPhone Contacts, as a result of different synchronization connections they have made with e.g. Exchange Servers.
How is it possible to get all of these different address books? I would be interested in getting the names under which these different address books are saved and accessing their contact information.
Thank you!
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef sourcesArray = ABAddressBookCopyArrayOfAllSources(addressBook);
for (CFIndex i = 0; i < CFArrayGetCount(sourcesArray); i++) {
ABRecordRef source = (ABRecordRef)CFArrayGetValueAtIndex(sourcesArray, i);
ABRecordID sourceID = ABRecordGetRecordID(source);
CFNumberRef sourceType = (CFNumberRef)ABRecordCopyValue(source, kABSourceTypeProperty);
CFStringRef sourceName = (CFStringRef)ABRecordCopyValue(source, kABSourceNameProperty);
NSLog(#"source id=%d type=%d name=%#", sourceID, [(NSNumber *)sourceType intValue], sourceName);
CFRelease(sourceType);
if (sourceName != NULL) CFRelease(sourceName); // some source names are NULL
}
CFRelease(sourcesArray);
CFRelease(addressBook);
Note that, as of iOS 4, not all sources return a name. You may provide your own names based on type.
Use ABAddressBookCopyArrayOfAllPeopleInSource(addressBook, source) to get entries in a source.
There is only a single, centralized address book database on iOS accessible by a set of C functions, see ABAddressBook and the iOS address book programming guide.
You may be referring to groups within that address book. In that case, you can get the list of groups using the ABAddressBookCopyArrayOfAllGroups function as described in the reference on ABGroup.
Using the 'AddressBook.framework' is it possible to filter out all companies (i.e. just people). For example, how would one modify the following code to remove companies:
ABAddressBookRef addressbook = ABAddressBookCreate();
CFArrayRef contacts = ABAddressBookCopyArrayOfAllPeople(addressbook);
I found that companies do not appear to be stored as groups (they are still returned with the above call). Thanks!
You are correct, companies are records/people in the Address Book.
Look up the value for the kABPersonFlags -- one of the flags is "show as company". Then just do a bitmask and compare.
if (([aPerson valueForProperty:kABPersonFlags] & kABShowAsMask) == kABShowAsCompany) {
// it's a company
} else {
// it's a person, resource, or room
}
I used the following references from Apple, which you should probably read as well:
Address Book Programming Guide for Mac OS X
Address Book Constants Reference
ABPerson Class Reference
EDIT: Sorry, the above is for Address Book on Mac OS X. Try this for iOS:
ABRecordRef aRecord = ... // Assume this exists
CFNumberRef recordType = ABRecordCopyValue(aRecord, kABPersonKindProperty);
if (recordType == kABPersonKindOrganization) {
// it's a company
} else {
// it's a person, resource, or room
}
The idea is the same: get the value of the person type property, and see what it tells you.
Used these Apple docs:
Address Book Programming Guide for iOS
ABPerson Reference
I want to store network carrier as a string (e.g. AT&T) for each contact in address book.
I found a method
addPropertiesAndTypes for creating a custom property. But I am not able to find any proper example to do this.
I am using following code to iterate through contact book records:
ABAddressBookRef addressBook = ABAddressBookCreate();
CFArrayRef addressArr = ABAddressBookCopyArrayOfAllPeople(addressBook);
CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
for(int i=0; i<nPeople; i++) {
ABRecordRef recref = CFArrayGetValueAtIndex(addressArr, i);
}
And my query is, the property will stay with value after app is closed. Are these property is getting saved in address book database?
Help needed.
Thanks.
Make sure you call ABAddressBookSave().