26

This code works fine with iOS5/iOS6 but not working with iOS7.

CFErrorRef *error = NULL;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
    //ABAddressBookRef addressBook = ABAddressBookCreate();
    CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
    CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);

    for(int i = 0; i < numberOfPeople; i++) {

        ABRecordRef person = CFArrayGetValueAtIndex( allPeople, i );

        NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
        NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
     //   NSLog(@"Name:%@ %@", firstName, lastName);

        ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
        NSString *phoneNumber;
        for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
            phoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);
            //   NSLog(@"phone:%@", phoneNumber);
        }
DipakSonara
  • 2,598
  • 3
  • 29
  • 34
  • 2
    And what *does* happen on iOS 7? – Christian Schnorr Sep 26 '13 at 11:39
  • CFArrayRef does not returning array. always returns an empty array – DipakSonara Sep 26 '13 at 11:58
  • 1
    Are you testing on simulator or on device? I noticed the iOS simulator had no contacts by default. When I added one, it was obvious that the code was indeed working for both iOS 6 and 7. – Paaske Nov 15 '13 at 10:35
  • 1
    Paaske, you are right! It is unbelievable, but ONLY AFTER THE UPDATE TO XCODE 5.0.2, the address book of the simulator is empty! Earlier, you could always test your app with the standard contacts "John Appleseed" etc. Now you have to insert your own addresses BY HAND EVERY TIME YOU RESET THE SIMULATOR! – Reinhard Männer Nov 20 '13 at 06:46
  • 1
    I posted the Swift version here: http://stackoverflow.com/a/33219114/745161 – Bojan Bozovic Oct 19 '15 at 16:02
  • @ReinhardMänner You can simply create a VCF file of all contacts in your mac and drag and drop it in simulator. No need to do it manually. – NSNoob Dec 04 '15 at 10:54
  • @NSNoob You are right, but I solved the problem differently: By now it is possible to use the contacts on any server, e.g. iCloud. – Reinhard Männer Dec 04 '15 at 11:15

2 Answers2

66

Today updated my example and removed memory leaks :)

 + (NSArray *)getAllContacts {

    CFErrorRef *error = nil;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
    ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
    CFArrayRef allPeople = (ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByFirstName));
    //CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
      CFIndex nPeople = CFArrayGetCount(allPeople); // bugfix who synced contacts with facebook
    NSMutableArray* items = [NSMutableArray arrayWithCapacity:nPeople];

    if (!allPeople || !nPeople) {
        NSLog(@"people nil");
    }


    for (int i = 0; i < nPeople; i++) {

        @autoreleasepool {

            //data model
            ContactsData *contacts = [ContactsData new];

            ABRecordRef person = CFArrayGetValueAtIndex(allPeople, i);

            //get First Name
            CFStringRef firstName = (CFStringRef)ABRecordCopyValue(person,kABPersonFirstNameProperty);
            contacts.firstNames = [(__bridge NSString*)firstName copy];

            if (firstName != NULL) {
                CFRelease(firstName);
            }


            //get Last Name
            CFStringRef lastName = (CFStringRef)ABRecordCopyValue(person,kABPersonLastNameProperty);
            contacts.lastNames = [(__bridge NSString*)lastName copy];

            if (lastName != NULL) {
                CFRelease(lastName);
            }


            if (!contacts.firstNames) {
                contacts.firstNames = @"";
            }

            if (!contacts.lastNames) {
                contacts.lastNames = @"";
            }



            contacts.contactId = ABRecordGetRecordID(person);
            //append first name and last name
            contacts.fullname = [NSString stringWithFormat:@"%@ %@", contacts.firstNames, contacts.lastNames];


            // get contacts picture, if pic doesn't exists, show standart one
            CFDataRef imgData = ABPersonCopyImageData(person);
            NSData *imageData = (__bridge NSData *)imgData;
            contacts.image = [UIImage imageWithData:imageData];

            if (imgData != NULL) {
                CFRelease(imgData);
            }

            if (!contacts.image) {
                contacts.image = [UIImage imageNamed:@"avatar.png"];
            }


            //get Phone Numbers
            NSMutableArray *phoneNumbers = [[NSMutableArray alloc] init];
            ABMultiValueRef multiPhones = ABRecordCopyValue(person, kABPersonPhoneProperty);

            for(CFIndex i=0; i<ABMultiValueGetCount(multiPhones); i++) {
                @autoreleasepool {
                    CFStringRef phoneNumberRef = ABMultiValueCopyValueAtIndex(multiPhones, i);
                    NSString *phoneNumber = CFBridgingRelease(phoneNumberRef);
                    if (phoneNumber != nil)[phoneNumbers addObject:phoneNumber];
                    //NSLog(@"All numbers %@", phoneNumbers);
                }
            }

            if (multiPhones != NULL) {
                CFRelease(multiPhones);
            }

            [contacts setNumbers:phoneNumbers];

            //get Contact email
            NSMutableArray *contactEmails = [NSMutableArray new];
            ABMultiValueRef multiEmails = ABRecordCopyValue(person, kABPersonEmailProperty);

            for (CFIndex i=0; i<ABMultiValueGetCount(multiEmails); i++) {
                @autoreleasepool {
                    CFStringRef contactEmailRef = ABMultiValueCopyValueAtIndex(multiEmails, i);
                    NSString *contactEmail = CFBridgingRelease(contactEmailRef);
                    if (contactEmail != nil)[contactEmails addObject:contactEmail];
                    // NSLog(@"All emails are:%@", contactEmails);
                }
            }

            if (multiEmails != NULL) {
                CFRelease(multiEmails);
            }

            [contacts setEmails:contactEmails];

            [items addObject:contacts];

#ifdef DEBUG
            //NSLog(@"Person is: %@", contacts.firstNames);
            //NSLog(@"Phones are: %@", contacts.numbers);
            //NSLog(@"Email is:%@", contacts.emails);
#endif

        }
    } //autoreleasepool
    CFRelease(allPeople);
    CFRelease(addressBook);
    CFRelease(source);
    return items;

}

Actually recently wrote pod in Swift 3 using CNContacts, to whom it may need

Swift ContactBook Picker

Anton
  • 3,102
  • 2
  • 28
  • 47
  • Your code has error like this " AB: Could not compile statement for query (ABCCopyArrayOfAllInstancesOfClassInSourceMatchingProperties): SELECT ROWID, Name, ExternalIdentifier, Type, ConstraintsPath, ExternalModificationTag, ExternalSyncTag, AccountID, Enabled, SyncData, MeIdentifier, Capabilities FROM ABStore WHERE Enabled = ?; " – DipakSonara Sep 26 '13 at 11:57
  • 2
    @DipakSonara I gave just example. not all my working code. But it's working and fetching all my contacts with pictures, phones and emails. You have create an array that contains only one contact. and then make iteration. anyway see my updated answer – Anton Sep 26 '13 at 12:02
  • 1
    I got the solution actually we need to get permission from user. http://stackoverflow.com/questions/12648244/programmatically-request-access-to-contacts-in-ios-6 – DipakSonara Sep 26 '13 at 12:56
  • 1
    @DipakSonara yes that's right. the same solution also in my code above. ABAddressBookRequestAccessWithCompletion – Anton Sep 26 '13 at 13:09
  • 5
    How is `ContactsData` defined? – Trav McKinney May 28 '14 at 16:56
  • @Anton - I was asking about the `ContactsData` class that you set `contacts` to with `ContactsData *contacts = [ContactsData new];`. Instead of using that class I just ended up saving the data in a dictionary. – Trav McKinney May 29 '14 at 14:51
  • @TravMcKinney You could do anything with that if it's working :) It's simple class that you can use in your other classes – Anton May 30 '14 at 15:17
  • 4
    @TravMcKinney ContactsData is my Class instance. interface ContactsData : NSObject property (strong, nonatomic) NSMutableArray *numbers; property (strong, nonatomic) NSMutableArray *emails; property (strong, nonatomic) UIImage* image; property (strong, nonatomic) NSString *firstNames; property (strong, nonatomic) NSString *lastNames; – Anton Jun 12 '14 at 21:44
  • 1
    I had a problem with your code. Hope helps someone http://stackoverflow.com/questions/24651715/what-abrecordcopyvalue-can-return-solving-a-bad-access#24651940 – Sasha Grievus Jul 09 '14 at 11:41
  • 1
    @ElisabettaFalivene I also had to change nPeople value by CFArrayGetCount(allPeople) instead of ABAddressBookGetPersonCount(addressBook) – Kevin Hirsch Sep 02 '14 at 10:14
  • 1
    ContactsData class and code should be explained. -1 – donkey Feb 14 '15 at 19:25
  • @ge0rges ContactsData it's array that getting contacts fetch class that returning an array with one record. Then you should do iteration to get all your records from your addressbook – Anton Feb 14 '15 at 20:19
  • @Anton Why do you require to define ABAddressBookRef twice. I'm asking because I want to define ABAddressBookRegisterExternalCallBack. – meteors Apr 03 '15 at 14:14
  • @meteors maybe it happened accidentally. You could remove once – Anton Apr 03 '15 at 14:37
14

Didn't write it myself. But it works with me:

ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);

if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
    ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
       ABAddressBookRef addressBook = ABAddressBookCreate( );
    });
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {

    CFErrorRef *error = NULL;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
    CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeople(addressBook);
    CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);

    for(int i = 0; i < numberOfPeople; i++) {

        ABRecordRef person = CFArrayGetValueAtIndex( allPeople, i );

       // NSString *firstName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonFirstNameProperty));
       // NSString *lastName = (__bridge NSString *)(ABRecordCopyValue(person, kABPersonLastNameProperty));
       // NSLog(@"Name:%@ %@", firstName, lastName);

        ABMultiValueRef phoneNumbers = ABRecordCopyValue(person, kABPersonPhoneProperty);
        [[UIDevice currentDevice] name];

        //NSLog(@"\n%@\n", [[UIDevice currentDevice] name]);

        for (CFIndex i = 0; i < ABMultiValueGetCount(phoneNumbers); i++) {
            NSString *phoneNumber = (__bridge_transfer NSString *) ABMultiValueCopyValueAtIndex(phoneNumbers, i);

            addressBookNum = [addressBookNum stringByAppendingFormat: @":%@",phoneNumber];
        }  
    }
    NSLog(@"AllNumber:%@",addressBookNum);
}
else {
    // Send an alert telling user to change privacy setting in settings app
}

and in my .h

@property NSString * addressBookNum;
dima
  • 365
  • 2
  • 17