0
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);

__block BOOL accessGranted = NO;
if (ABAddressBookRequestAccessWithCompletion != NULL) { // we're on iOS 6
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);
    ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
        accessGranted = granted;
        dispatch_semaphore_signal(sema);
    });
    dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);

}
else { // we're on iOS 5 or older
    accessGranted = YES;
}

if (accessGranted) {

//  ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
    ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
    CFArrayRef allPeople = ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByFirstName);
    CFIndex nPeople = ABAddressBookGetPersonCount(addressBook);
    NSMutableArray* items = [NSMutableArray arrayWithCapacity:nPeople];
}

I used this code to fetch all contacts but ABAddressBookGetPersonCount is working fine but ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering is not working.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 1
    What is the exact problem. We can not help you with incomplete code. – Vivek Jan 30 '15 at 06:14
  • I don't know which problem he's concerned about, but one problem is that he's getting the contacts from the default source, but then retrieving an a count of all people within the whole address book. Thus `nPeople` could exceed the count of `allPeople`. – Rob Jan 30 '15 at 06:42

1 Answers1

3

A couple of issues:

  1. You can simplify the getting the array of people, e.g.:

    if (accessGranted) {
        ABRecordRef source = ABAddressBookCopyDefaultSource(addressBook);
        NSArray *allPeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeopleInSourceWithSortOrdering(addressBook, source, kABPersonSortByFirstName));
        NSUInteger nPeople = [allPeople count];
    
        // do what you want with the array
    
        // don't forget to release `source`; because we bridged allPeople, ARC will take care of that
    
        CFRelease(source);
    }
    
  2. You are, most likely, not capturing the open error correctly. It should be:

    CFErrorRef error = NULL;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
    if (!addressBook) {
        NSLog(@"Error opening address book: %@", CFBridgingRelease(error));
        return;
    }
    

    Note, what you want to avoid is

    CFErrorRef *error = NULL;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, error);
    

    You'll see this pattern occasionally on Stack Overflow, but it is not correct. That will never return an error object.

  3. You should not use a semaphore like that, because you're blocking the main thread while you wait for the user to grant permission that first time. If you do that at the wrong time, the watchdog process may kill your app.

    Instead, employ asynchronous patterns, putting the code that should be run after the user grants permission inside the completion block parameter of the ABAddressBookRequestAccessWithCompletion call. But never use semaphores in conjunction with this asynchronous permissions API.

  4. You probably already know how to do this, but for example, to get the names of the people, you can do:

    for (NSInteger i = 0; i < nPeople; i++) {
        ABRecordRef person = (__bridge ABRecordRef)allPeople[i];
        NSString *firstName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonFirstNameProperty));
        NSString *lastName = CFBridgingRelease(ABRecordCopyValue(person, kABPersonLastNameProperty));
        NSLog(@"%@ %@", firstName, lastName);
    }
    
  5. Remember to run your code through the static analyzer (shift+command+B, or "Analyze" on Xcode's "Product" menu) which is really good at identifying leaks in your code that can plague address book code.

    Just remember the "Create Rule", namely that any time you call a Core Foundation function with Create or Copy in the name, you're responsible for releasing the object, either calling CFRelease or transferring ownership to ARC with CFBridgingRelease (or __bridge_transfer).

Rob
  • 415,655
  • 72
  • 787
  • 1,044