iOS: How To Get The Person Out Of ABAddressBookRef

One of the reasons I made an offer to build an iPhone app for anyone for $200 was so I could continue learning. The more variety in the projects, the better, since that means I get to experiment with different parts of the iOS SDK.

Currently, I’m working on an app that requires me to use and store address book contacts. Seems like something that should be pretty simple, right?! Well, that’s what I though until I started working with ABAddressBookRef. To my surprise, working with ABAddressBookRef is more like working with C instead of Objective-C. Times like this is when I wish I’d taken Stanford’s CS107A. Maybe I still will!

Anyway, after a bit of struggle, I wanted to share how I got the person’s name and record id from the address book, so I could store it in locally in the app I’m building.

// ViewController.m

#import "ViewController.h"

// make sure to import the AddressBook framework
#import <AddressBook/AddressBook.h>

// I'm using Core Data to store the Person locally
// this is my Person category
#import "Person+WOLExtensions.h"

@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];

    [self loadPeopleFromAddressBook];
}

- (void)loadPeopleFromAddressBook
{
    CFErrorRef error = nil;
    ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, &error);
    [Person retrievePeopleFromAddressBook:addressBook];
}

// your other custom implementation here
@end

// Person+WOLExtensions.m

#import "Person+WOLExtensions.h"

@implementation Person (WOLExtensions)

+ (void)retrievePeopleFromAddressBook:(ABAddressBookRef)addressBook
{
    ABAddressBookRequestAccessWithCompletion(addressBook, ^(bool granted, CFErrorRef error) {
        if (error || !granted)
        {
            // handle error
            NSLog(@"ERROR: %@", error);
        }
        else
        {
            CFArrayRef peopleFromAddressBook = ABAddressBookCopyArrayOfAllPeople(addressBook);
            CFIndex numberOfPeople = ABAddressBookGetPersonCount(addressBook);

            // using Magical Record to get all the people from CoreData
            NSArray *peopleArray = [Person MR_findAll];

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

                ABRecordRef personRecord = CFArrayGetValueAtIndex(peopleFromAddressBook, i);

                NSNumber *recordId = [Person recordIdFromPersonRecord:personRecord];

                if ([self isUniqueRecord:recordId peopleArray:peopleArray])
                {
                    Person *person = [Person MR_createEntity];
                    person.recordId = recordId;
                    person.name = [Person nameFromPersonRecord:personRecord];
                }
            }

        }

        [[NSManagedObjectContext MR_contextForCurrentThread] MR_saveWithOptions:MRSaveParentContexts completion:nil];
    });
}

+ (BOOL)isUniqueRecord:(NSNumber *)recordId peopleArray:(NSArray *)peopleArray
{
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"recordId = %@", recordId];
    NSArray *duplicates = [peopleArray filteredArrayUsingPredicate:predicate];
    return duplicates.count == 0;
}

+ (NSNumber *)recordIdFromPersonRecord:(ABRecordRef)personRecord
{
    ABRecordID recordId = ABRecordGetRecordID(personRecord);
    return [NSNumber numberWithInt:(int)recordId];
}

+ (NSString *)nameFromPersonRecord:(ABRecordRef)personRecord
{
    ABMultiValueRef name = ABRecordCopyCompositeName(personRecord);
    return (__bridge NSString *)name;
}

If you can think of a better way to do this, please let me know in the comments?

Enjoy the article? Join over 14,500+ Swift developers and enthusiasts who get my weekly updates.

  • Grateful

    You were the only tutorial I found that specified you needed to call ABAddressBookRequestAccessWithCompletion: and for that I thank you.

  • ChrisHaze

    Natasha, thanks for the great post! There’s very little online depicting how to create a address book copy. I am debating on using a sqlite database, but I feel like core data would be the best bet. Do you still feel that the route you took here is the best route to take? – I’m going a different route (CreationDate Property), but it’s very similar.