I am currently writing an app which should enable the user to modify the contact details (mainly numbers) in the app, and then these modifications should be reflected directly to the Address Book.
I searched thoroughly on the internet, but all the examples I found were either to load the contacts or add new contact, but nothing on modifying an existing contact.
also how can I get a list of all phone numbers of a single contact, in case he has several numbers stored.
To allow a user to edit their details directly, see Apple's documentation on displaying and editing a person record. The initial section of that says, "Set the delegate, which must adopt the ABPersonViewControllerDelegate protocol. To allow the user to edit the record, set allowsEditing to YES."
For example:
ABPersonViewController *personViewController = [[ABPersonViewController alloc] init];
personViewController.personViewDelegate = self;
personViewController.allowsEditing = YES;
Other than setting allowsEditing, the code would be exactly the same as that required to display a person's details without editing. (This code was drawn from this answer which displays a fuller example regarding deleting a contact from the address book.)
However, I see your title refers to doing so programatically. Apple's Address Book Programing Guide for iOS says, "Remember that the Address Book database is ultimately owned by the user, so applications must be careful not to make unexpected changes to it. Generally, changes should be initiated or confirmed by the user."
However, it is possible. The following example appears on p.17 of that document:
ABRecordRef aRecord = ABPersonCreate();
CFErrorRef anError = NULL;
bool didSet;
didSet = ABRecordSetValue(aRecord, kABPersonFirstNameProperty, CFSTR("Katie"), &anError);
if (!didSet) {/* Handle error here. */}
didSet = ABRecordSetValue(aRecord, kABPersonLastNameProperty, CFSTR("Bell"), &anError);
if (!didSet) {/* Handle error here. */}
CFStringRef firstName, lastName;
firstName = ABRecordCopyValue(aRecord, kABPersonFirstNameProperty);
lastName = ABRecordCopyValue(aRecord, kABPersonLastNameProperty);
/* ... Do something with firstName and lastName. ... */
CFRelease(aRecord);
CFRelease(firstName);
CFRelease(lastName);
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
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
When I try to retrieve phone numbers from Address Book contacts, the result is always nil if the contact has an e-mail address. This is obviously a big problem...such a big one, in fact, that I have a hard time believing I'm not doing something wrong. But every test I've done--including using other, unrelated code samples--gives the same result.
I've used the code below to successfully retrieve email addresses:
+ (NSArray*)emailFromRecordRef:(ABRecordRef)personRef
{
ABMultiValueRef emailAddressesRef = ABRecordCopyValue(personRef, kABPersonEmailProperty);
NSArray* emailAddresses = nil;
if (emailAddressesRef != nil)
{
emailAddresses = [(NSArray*)ABMultiValueCopyArrayOfAllValues(emailAddressesRef) autorelease];
CFRelease(emailAddressesRef);
}
return emailAddresses;
}
...but when I adapt the same code to retrieve phone numbers:
+ (NSArray*)phoneNumbersFromRecordRef:(ABRecordRef)personRef
{
ABMultiValueRef phoneNumbersRef = ABRecordCopyValue(personRef, kABPersonPhoneProperty);
NSArray* phoneNumbers = nil;
if (phoneNumbersRef != nil)
{
phoneNumbers = [(NSArray*)ABMultiValueCopyArrayOfAllValues(phoneNumbersRef) autorelease];
CFRelease(phoneNumbersRef);
}
return phoneNumbers;
}
...it only retrieves phone numbers for users that don't have email addresses. If they have email addresses, no phone numbers are returned.
Has anyone out there successfully retrieved phone numbers from Address Book contacts?
Thanks.
Turns out, of course, I was doing something wrong. Apparently you need to keep the reference to AddressBook alive (retained) during the entire time you're requesting additional attributes like phone numbers. When I kept it alive, the phone number issue went away.
Is there any way to edit a contact programmatically in the iPhone? For example, I want to modify a contact which is returned by ABPeoplePickerNavigationController. How can I do this? I have:
(BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person
{
NSString *phoneNum = #"01234567890";
ABAddressBookRef addressBook = ABAddressBookCreate();
contact = ABAddressBookGetPersonWithRecordID(addressBook, ABRecordGetRecordID(person));
if(phoneNum && contact != NULL)
{
ABMutableMultiValueRef phoneNumberMultiValue = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(phoneNumberMultiValue, phoneNum, kABPersonPhoneMobileLabel, NULL);
[self dismissModalViewControllerAnimated:YES];
ABRecordSetValue(contact, kABPersonPhoneProperty, phoneNumberMultiValue, nil);
//THIS CODE IS NOT BEHAVING AS I EXPECTED,
//if(ABAddressBookHasUnsavedChanges(addressBook))//Anyway this needs to be checked
{
ABAddressBookSave(addressBook, NULL);
}
}
return NO;
}
The call to ABAddressBookSave(addressBook, NULL); is working. But the problem is, all the old information of the "contact" is removed from the address book and only the "phoneNum" is saved at the end.
How can I solve this problem?
Apologies for the previous answer - I read the question wrong...! I thought that the contact wasn't saving at all on the address book.
It seems that the only information lost is the contact's other phone numbers, correct? Meaning that the ABRecordRef's other details, such as address, notes, birthdates, etc. remain intact.
If this is correct, I assume that the only feasible way to counter this deletion of other phone numbers is to simply append a PhoneLabel to the contact's existing kABPersonPhoneProperty. Instead of creating a new ABMutableMultiValueRef for the phone numbers, copy it from the record like so:
ABMutableMultiValueRef phoneNumberMultiValue = ABMultiValueCreateMutableCopy((ABMultiValueRef)ABRecordCopyValue(contact, kABPersonPhoneProperty));
With this, you can now append the new phone number with the same code:
ABMultiValueAddValueAndLabel(phoneNumberMultiValue, phoneNum, kABPersonPhoneMobileLabel, NULL);
What's neat about this is that no replacing or deletion occurs - the app simply adds another phone number to the contact's phone numbers property. :D
Hope this helps.
The first thing you should do is pass in a reference to a CFErrorRef as the second argument to ABAddressBookSave; it may be that it's trying to report an error, but unable to, since you don't give it this argument.
Currently there is no way to EDIT and SAVE the contact to the address book. The IPhone API does not support this requirement. In order to EDIT a specific contact we need to get a reference of that contact by record ID and copy all the information to the reference and then save that contact to the address book.