Using an ABRecordRef #property triggers EXC_BAD_ACCESS - iphone

I'm trying to implement some code that writes to a contact using code from this Address Book guide. I've declared a property in my ViewController.h:
#property ABRecordRef mABRecordRef;
Then I retrieve a record:
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person {
At this point, I am able to successfully call ABRecordSetValue:
CFErrorRef anError = NULL;
bool didSet;
didSet = ABRecordSetValue(person, kABPersonFirstNameProperty, (CFStringRef)#"foo", &anError);
if( didSet )
{
// ...
In this method, I also save the record:
self.mABRecordRef = person;
After some user action (in another method), I attempt to use this record in the same way:
CFErrorRef anError = NULL;
bool didSet;
didSet = ABRecordSetValue(self.ABRecordRef, kABPersonFirstNameProperty, (CFStringRef)#"foo", &anError);
But I get an error:
EXC_BAD_ACCESS(code=2, address=0x...
Why is this? I am assuming (perhaps incorrectly) that my ABRecordRef isn't released between my first and second uses.
I've also tried saving off that record's UniqueId as well as peoplePicker.addressBook, then later on pulling the same record back, but I get a similar error.

Related

pick a contact from iPhone's address book and add a new phone number into it

My scenario is to pick a contact from iPhone's address book and display it's name and first phone number into textfields and meanwhile programmatically add a phone number in its KABOtherLabel property.
I am using this code to add a contact programmatically;
-(IBAction)addContactToAddressBook:(id)sender
{
CFErrorRef error = NULL;
ABAddressBookRef iPhoneAddressBook = ABAddressBookCreateWithOptions(NULL, NULL);
ABRecordRef newPerson = ABPersonCreate();
ABRecordSetValue(newPerson, kABPersonFirstNameProperty, (__bridge CFTypeRef)(contactName.text), &error);
ABMutableMultiValueRef multiPhone = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(multiPhone, (__bridge CFTypeRef)(phoneNumber.text), kABPersonPhoneMobileLabel, NULL);
ABMultiValueAddValueAndLabel(multiPhone, #"1-123-456-7890", kABPersonPhoneIPhoneLabel, NULL);
ABMultiValueAddValueAndLabel(multiPhone, #"1-987-654-3210", kABOtherLabel, NULL);
ABRecordSetValue(newPerson, kABPersonPhoneProperty, multiPhone,nil);
CFRelease(multiPhone);
ABAddressBookAddRecord(iPhoneAddressBook, newPerson, &error);
ABAddressBookSave(iPhoneAddressBook, &error);
if (error != NULL)
{
NSLog(#"Some error...");
}
}
This code works perfectly.
And Im using following code to pick up a contact and display it in the text fields;
-(IBAction)pickContact:(id)sender
{
ABPeoplePickerNavigationController *picker =
[[ABPeoplePickerNavigationController alloc] init];
picker.peoplePickerDelegate = self;
[self presentViewController:picker animated:YES completion:nil];
}
//called when user presses the cancel button in the Address book view controller
- (void)peoplePickerNavigationControllerDidCancel:(ABPeoplePickerNavigationController *)peoplePicker
{
[self dismissViewControllerAnimated:YES completion:nil];
}
//called when user pics up a contact from the phone's address book
- (BOOL)peoplePickerNavigationController:
(ABPeoplePickerNavigationController *)peoplePicker
shouldContinueAfterSelectingPerson:(ABRecordRef)person {
[self displayPerson:person]; //calls displayPerson:(ABRecordRef)person to show contact's information in the app
[self dismissViewControllerAnimated:NO completion:NULL];
}
- (void)displayPerson:(ABRecordRef)person
{
NSString* name = (__bridge_transfer NSString*)ABRecordCopyValue(person,kABPersonFirstNameProperty); //Extracts the contact's first name from address book & assigns it to a string value
//NSString* lastName = (__bridge_transfer NSString*)ABRecordCopyValue(person,kABPersonLastNameProperty); //Extracts the contact's last name from address book & assigns it to a string value
self.contactName.text = name;
NSString* phone = nil;
//Extracts the first phone number among multiple contact numbers from address book & assigns it to a string value
ABMultiValueRef phoneNumbers = ABRecordCopyValue(person,kABPersonPhoneProperty);
if (ABMultiValueGetCount(phoneNumbers) > 0)
{
phone = (__bridge_transfer NSString*)
ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
}
else
{
phone = #"[None]";
}
self.phoneNumber.text = phone;
}
This code also works perfectly and I get the values in the required text fields.
However when I combine these both codes i-e write the following code at the end of displayPerson() method or call it from shouldContinueAfterSelectingPerson
The code I used to add detail in existing contact is as follows;
CFErrorRef error = NULL;
ABAddressBookRef myAddressBook = ABAddressBookCreateWithOptions(NULL, NULL);
ABMutableMultiValueRef multiPhone = ABMultiValueCreateMutable(kABMultiStringPropertyType);
ABMultiValueAddValueAndLabel(multiPhone, #"1-987-654-3210", kABPersonPhoneIPhoneLabel, NULL);
ABRecordSetValue(person, kABPersonPhoneProperty, multiPhone,nil);
CFRelease(multiPhone);
ABAddressBookAddRecord(myAddressBook, person, &error);
ABAddressBookSave(myAddressBook, &error);
if (error != NULL)
{
NSLog(#"Some error");
}
return NO;
}
The problem is that only this number is added into the contact and all the other existing entries get removed. i-e this number overwrites all the other existing entries.
Please help
ABRecordSetValue() sets the property of the record to the value you give it, overwriting any existing value for said property. Because you use ABMultiValueCreateMutable() to create a new, empty value list, you effectively ignore any values of kABPersonPhoneProperty that may already exist on the ABRecord.
Instead, your process for editing an existing record should be:
Call ABRecordCopyValue() on a chosen record to get the existing value list
Call ABMultiValueCreateMutableCopy() to get a mutable value list
Add the desired phone number to the mutable value list with ABMultiValueAddValueAndLabel()
Write the mutable value list with the phone number added back to the record with ABRecordSetValue()

Is this the right way to delete a contact from the iPhone?

I am trying to delete a contact from the iPhone which I have created. I tried to find a good working example of deleting a contact, however didn't find one. Adding a contact seemed quite easy but deleting one seems hard. The following code does not work, but it seemed to be plausible:
ABAddressBookRef addressBook = ABAddressBookCreate();
ABRecordRef delete = ABPersonCreate();
ABRecordSetValue(delete, kABPersonFirstNameProperty, #"Max", nil);
ABRecordSetValue(delete, kABPersonLastNameProperty, #"Mustermann", nil);
ABAddressBookRemoveRecord(addressBook, delete, &error);
ABAddressBookSave(addressBook, &error);
Could anyone help me out.
Thanks in advance for your help.
Max
The problem is that you're creating an ABRecord that isn't inside of the address book. What you have to do is search through an array of ABRedords from the ABAddressBook. I wrote how to do this for you:
CFErrorRef error = nil;
ABAddressBookRef addressBook = ABAddressBookCreate();
__block ABRecordRef toDelete = ABPersonCreate();
ABRecordSetValue(toDelete, kABPersonFirstNameProperty, #"Max", nil);
ABRecordSetValue(toDelete, kABPersonLastNameProperty, #"Mustermann", nil);
// Gets the array of everybody in the address book
NSArray *peopleArray = (__bridge NSArray *) ABAddressBookCopyArrayOfAllPeople(addressBook);
// Creates a test predicate to see if the ABRecord has the same name as toDelete
BOOL (^predicate)(id obj, NSUInteger idx, BOOL *stop) = ^(id obj, NSUInteger idx, BOOL *stop) {
ABRecordRef person = (__bridge ABRecordRef)obj;
CFComparisonResult result = ABPersonComparePeopleByName(person, delete, kABPersonSortByLastName);
bool pass = (result == kCFCompareEqualTo);
if (pass) {
toDelete = person;
}
return (BOOL) pass;
};
int idx = [peopleArray indexOfObjectPassingTest:predicate];
bool removed = ABAddressBookRemoveRecord(addressBook, toDelete, &error);
bool saved = ABAddressBookSave(addressBook, &error);
You can change how you want to compare ABRecord instances by changing the block code. All it's doing now is comparing the names of the contacts.
A caveat with this code is that it will only delete one instance of the ABRecords whose name matches delete’s.

kABPersonAddressProperty causes crashes under ABAddressBookGetPersonWithRecordID

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

Reading email address from contacts fails with weird memory issue

I'm stumped.
I'm trying to get a list of all the email address a person has.
I'm using the ABPeoplePickerNavigationController to select the person, which all seems fine. I'm setting my
ABRecordRef personDealingWith;
from the person argument to
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person property:(ABPropertyID)property identifier:(ABMultiValueIdentifier)identifier {
and everything seems fine up till this point.
The first time the following code executes, all is well.
When subsequently run, I can get issues. First, the code:
// following line seems to make the difference (issue 1)
// NSLog(#"%d", ABMultiValueGetCount(ABRecordCopyValue(personDealingWith, kABPersonEmailProperty)));
// construct array of emails
ABMultiValueRef multi = ABRecordCopyValue(personDealingWith, kABPersonEmailProperty);
CFIndex emailCount = ABMultiValueGetCount(multi);
if (emailCount > 0) {
// collect all emails in array
for (CFIndex i = 0; i < emailCount; i++) {
CFStringRef emailRef = ABMultiValueCopyValueAtIndex(multi, i);
[emailArray addObject:(NSString *)emailRef];
CFRelease(emailRef);
}
}
// following line also matters (issue 2)
CFRelease(multi);
If compiled as written, the are no errors or static analysis problems. This crashes with a
*** -[Not A Type retain]: message sent to deallocated instance 0x4e9dc60
error.
But wait, there's more! I can fix it in either of two ways.
Firstly, I can uncomment the NSLog at the top of the function. I get a leak from the NSLog's ABRecordCopyValue every time through, but the code seems to run fine.
Also, I can comment out the
CFRelease(multi);
at the end, which does exactly the same thing. Static compilation errors, but running code.
So without a leak, this function crashes. To prevent a crash, I need to haemorrhage memory. Neither is a great solution.
Can anyone point out what's going on?
It turned out that I wasn't storing the ABRecordRef personDealingWith var correctly. I'm still not sure how to do that properly, but instead of having the functionality in another routine (performed later), I'm now doing the grunt-work in the delegate method, and using the derived results at my leisure. The new (working) routine:
- (BOOL)peoplePickerNavigationController:(ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person {
// as soon as they select someone, return
personDealingWithFullName = (NSString *)ABRecordCopyCompositeName(person);
personDealingWithFirstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
// construct array of emails
[personDealingWithEmails removeAllObjects];
ABMutableMultiValueRef multi = ABRecordCopyValue(person, kABPersonEmailProperty);
if (ABMultiValueGetCount(multi) > 0) {
// collect all emails in array
for (CFIndex i = 0; i < ABMultiValueGetCount(multi); i++) {
CFStringRef emailRef = ABMultiValueCopyValueAtIndex(multi, i);
[personDealingWithEmails addObject:(NSString *)emailRef];
CFRelease(emailRef);
}
}
CFRelease(multi);
return NO;
}
I ran into a similar problem. The problem might lie at how you set your
ABRecordRef personDealingWith;
It seems that you can't just do:
ABRecordRef personDealingWith = person;
Because personDealingWith remains null. Instead what I did is:
ABRecordID personID = ABRecordGetRecordID(person);
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
personDealingWith = ABAddressBookGetPersonWithRecordID(addressBook, personID);

How can I add custom fields to contacts info in iphone contact list

I am developing a app in which I have to add custom contacts to iPhone Contact list.
To elaborate that every contact in iPhone have a definite set of fields which we can be used to save contact info.
I want to know if we can
add custom fields apart from that ofexisting options in iPhone.
If its possible please show me the way to do that, googled it but didn't found any thing meaningfull.
Thanks in advance.
From this.
ABRecordRef aRecord = ABPersonCreate();
CFErrorRef anError = NULL;
ABRecordSetValue(aRecord, kABPersonFirstNameProperty,
CFSTR("Jijo"), &anError);
ABRecordSetValue(aRecord, kABPersonLastNameProperty,
CFSTR("Pulikkottil"), &anError);
if (anError != NULL) {
NSLog(#"error while creating..");
}
CFStringRef firstName, lastName;
firstName = ABRecordCopyValue(aRecord, kABPersonFirstNameProperty);
lastName = ABRecordCopyValue(aRecord, kABPersonLastNameProperty);
ABAddressBookRef addressBook;
CFErrorRef error = NULL;
addressBook = ABAddressBookCreate();
BOOL isAdded = ABAddressBookAddRecord (
addressBook,
aRecord,
&error
);
if(isAdded){
NSLog(#"added..");
}
if (error != NULL) {
NSLog(#"ABAddressBookAddRecord %#", error);
}
error = NULL;
BOOL isSaved = ABAddressBookSave (
addressBook,
&error
);
if(isSaved){
NSLog(#"saved..");
}
if (error != NULL) {
NSLog(#"ABAddressBookSave %#", error);
}
CFRelease(aRecord);
CFRelease(firstName);
CFRelease(lastName);
CFRelease(addressBook);
If you need to store data in there, I think your only option is kABPersonNoteProperty, but I'm no expert on this.
Edit: link.
Answer: nope!
Edit: you can also prompt the user to add an address book entry as done here.
It is not possible to programmatically add custom fields to the iPhone address book, even in iOS 5.
Please check this out!
How to add a "Custom Label" to iOS AddressBook programmatically?
Working 100% in iOS 6. Tested!