Donal Rafferty
Donal Rafferty

Reputation: 19826

iPhone address book problem

I'm having a bit of an issue with using the address book to get the names of the contacts from the device into my own contacts view within my application.

The code I have works fine on the emulator but I when tested on an iPhone 4 it will crash, the application seems to work fine if there are two or less contacts but 3 or more and the application crashes.

Here is the code I am using to get the names of contacts into an array.

    ABAddressBookRef addressBook;
    bool wantToSaveChanges = YES;
    bool didSave;
    CFErrorRef error = NULL;

    addressBook = ABAddressBookCreate();

    listOfContacts = [[NSMutableArray alloc]init]; 

    int i;
    int len = (int) ABAddressBookGetPersonCount(addressBook);
    for(i = 1; i< (len+1); i++){

        ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID) i);
        NSString* name = (NSString *)ABRecordCopyCompositeName(person);
        ABMultiValueRef number = (NSString *)ABRecordCopyValue(person,kABPersonPhoneProperty);
        NSString *mobileNum = (NSString *)ABMultiValueCopyValueAtIndex(number, 0 );

        NSLog(@"Name = %@", name);
        NSLog(@"Number = %@", mobileNum);

        [listOfContacts addObject:name]; 

        [name release];
        [mobileNum release];


    }


    if(ABAddressBookHasUnsavedChanges(addressBook)){
        if(wantToSaveChanges){
            didSave = ABAddressBookSave(addressBook, &error);
            if(!didSave){
                //Error
            }
        }
        else{
            ABAddressBookRevert(addressBook);
        }
    }

When it crashes this is the line that gets highlighted in Xcode:

NSString* name = (NSString *)ABRecordCopyCompositeName(person);

And the error states:

Thread 1: Program received signal: "EXC_BAD_ACCESS"

Can anyone see what the problem might be? I dont understand why it would work on the emulator but not on the device? And also why it works for up to two contacts but not 3 or more??

Upvotes: 0

Views: 3614

Answers (3)

Here is how I solved it:
(Note how I make sure to call only active records, also I created a customized Contact class)
This code takes care of edge case like: email/phone doesn't exist, or exists more than once...

+(NSArray *)getAddressBook{
    ABAddressBookRef addressBook;
    bool wantToSaveChanges = YES;
    bool didSave;
    CFErrorRef error = NULL;

    addressBook = ABAddressBookCreate();

    NSMutableArray *listOfContacts = [[NSMutableArray alloc]init]; 
    CFArrayRef array=ABAddressBookCopyArrayOfAllPeople(addressBook);

    int len=CFArrayGetCount(array);

    for (int i = 0; i<len; i++){
        ABRecordRef person = CFArrayGetValueAtIndex(array, i);
        if (ABRecordGetRecordType(person)==kABPersonType){

            NSString *firstName = (NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);
            NSString *lastName = (NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);

            ABMultiValueRef emails = (ABMultiValueRef)ABRecordCopyValue(person,kABPersonEmailProperty);
            ABMultiValueRef numbers = (ABMultiValueRef)ABRecordCopyValue(person,kABPersonPhoneProperty);

            int sumEmails=ABMultiValueGetCount(emails);
            int sumNumbers=ABMultiValueGetCount(numbers);

            for (int j=0; j<(sumNumbers>sumEmails?sumNumbers:sumEmails); j++) {
                ACL_AB_Contact *contact=[[ACL_AB_Contact alloc]initWithFirstName:firstName LastName:lastName];

                if (j<sumEmails){
                    contact.emailAddress=(NSString *)ABMultiValueCopyValueAtIndex(emails,j);
                }

                if (j<sumNumbers){
                    contact.phoneNumber=(NSString *)ABMultiValueCopyValueAtIndex(numbers,j);
                }
                [contact logContact];
                [listOfContacts addObject:contact]; 
                [contact release];
            }

        }
    }

    if(ABAddressBookHasUnsavedChanges(addressBook)){
        if(wantToSaveChanges){
            didSave = ABAddressBookSave(addressBook, &error);
            if(!didSave){
                //Error
            }
        }
        else{
            ABAddressBookRevert(addressBook);
        }
    }
    return [listOfContacts autorelease];
}

Upvotes: 2

Matthias Bauch
Matthias Bauch

Reputation: 90117

Just a guess:

ABRecordRef person = ABAddressBookGetPersonWithRecordID(addressBook, (ABRecordID) i);

This line looks fishy for me. I doubt that the record IDs are numbered from 1 to whatever. Especially if you have deleted an entry.

This would explain why it works on the simulator, I guess you just added some test contacts and never deleted one.

Upvotes: 4

Ruben Marin
Ruben Marin

Reputation: 1637

The record IDs are dynamic. It means that if you add 2 contacts and then remove the first, you will have only a contact with id "2". So I wouln't use a for statement to get through the contacts. Follow the Address Book Programming Guide

Upvotes: 1

Related Questions