Skip to content

Instantly share code, notes, and snippets.

@danielt1263
Last active August 29, 2015 14:09
Show Gist options
  • Save danielt1263/962d77cef0fff5c836fb to your computer and use it in GitHub Desktop.
Save danielt1263/962d77cef0fff5c836fb to your computer and use it in GitHub Desktop.
Easily access a read-only array of all the people in the contacts database for iOS. (Because Apple STILL hasn't made an ABAddressBook class for iOS like they did for MacOS.
#import <Foundation/Foundation.h>
extern NSString *addressBookErrorDomain;
typedef NS_ENUM(NSInteger, AddressBookError) {
AddressBookErrorOK,
AddressBookErrorNoAddressBook,
AddressBookErrorNoAccess
};
extern NSString *AddressBookChangedNotification;
@interface AddressBook : NSObject
@property (nonatomic, copy, readonly) NSArray *people;
+ (void)performAction:(void (^)(AddressBook *addressBook, NSError *error))action;
@end
#import "AddressBook.h"
@import AddressBook;
#import "PersonRecord.h"
NSString *addressBookErrorDomain = @"com.hanekedesign.addressbookerror";
NSString *AddressBookChangedNotification = @"com.hanekedesign.addressbookchanged";
static void addressBookCallback(ABAddressBookRef addressBookRef, CFDictionaryRef info, void* context) {
[[NSNotificationCenter defaultCenter] postNotificationName:AddressBookChangedNotification object:nil];
}
static ABAddressBookRef getAddressBookRef() {
static ABAddressBookRef addressBookRef;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
CFErrorRef* error = NULL;
addressBookRef = ABAddressBookCreateWithOptions(NULL, error);
if (error) {
addressBookRef = NULL;
}
else {
ABAddressBookRegisterExternalChangeCallback(addressBookRef, &addressBookCallback, NULL);
}
});
return addressBookRef;
}
@interface AddressBook ()
@property (nonatomic, assign) ABAddressBookRef addressBookRef;
@property (nonatomic, assign, readonly) BOOL isValid;
@end
@implementation AddressBook
+ (void)performAction:(void (^)(AddressBook *addressBook, NSError *error))action {
AddressBook *addressBook = [[AddressBook alloc] init];
if (addressBook.isValid) {
ABAddressBookRequestAccessWithCompletion(addressBook.addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {
action(addressBook, nil);
}
else {
NSError *error = [NSError errorWithDomain:addressBookErrorDomain code:AddressBookErrorNoAccess userInfo:nil];
action(nil, error);
}
});
}
else {
NSError *error = [NSError errorWithDomain:addressBookErrorDomain code:AddressBookErrorNoAddressBook userInfo:nil];
action(nil, error);
}
}
- (instancetype)init {
self = [super init];
if (self) {
self.addressBookRef = getAddressBookRef();
}
return self;
}
- (BOOL)isValid {
return self.addressBookRef != NULL;
}
- (NSArray *)people {
NSMutableArray * result = [[NSMutableArray alloc] init];
NSArray *basePeople = CFBridgingRelease(ABAddressBookCopyArrayOfAllPeople(self.addressBookRef));
for (int i = 0; i < basePeople.count; ++i) {
ABRecordRef record = (__bridge ABRecordRef)(basePeople[i]);
PersonRecord *personRecord = [[PersonRecord alloc] init];
personRecord.compositeName = CFBridgingRelease(ABRecordCopyCompositeName(record)) ?: @"";
personRecord.organization = CFBridgingRelease(ABRecordCopyValue(record, kABPersonOrganizationProperty)) ?: @"";
personRecord.jobTitle = CFBridgingRelease(ABRecordCopyValue(record, kABPersonJobTitleProperty)) ?: @"";
personRecord.emails = [self multiValuesFromRecord:record forProperty:kABPersonEmailProperty];
personRecord.phones = [self multiValuesFromRecord:record forProperty:kABPersonPhoneProperty];
[result addObject:personRecord];
}
return result;
}
- (NSArray *)multiValuesFromRecord:(ABRecordRef)record forProperty:(ABPropertyID)property {
ABMultiValueRef baseValues = ABRecordCopyValue(record, property);
NSMutableArray *values = [[NSMutableArray alloc] initWithCapacity:ABMultiValueGetCount(baseValues)];
for (int i = 0; i < ABMultiValueGetCount(baseValues); ++i) {
MultiValue *multiValue = [[MultiValue alloc] init];
multiValue.label = CFBridgingRelease(ABMultiValueCopyLabelAtIndex(baseValues, i)) ?: @"";
multiValue.value = CFBridgingRelease(ABMultiValueCopyValueAtIndex(baseValues, i)) ?: @"";
[values addObject:multiValue];
}
CFRelease(baseValues);
return values;
}
@end
@objc
class ExampleUsingTheAddressBookClass {
var people: [PersonRecord] = []
init() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("addressBookChangedNotification:"), name: AddressBookChangedNotification, object: nil)
loadPeople()
}
func addressBookChangedNotification(notification: NSNotification) {
loadPeople()
}
func loadPeople() {
AddressBook.performAction { addressBook, error in
if let addressBook = addressBook {
people = addressBook.people
}
}
}
}
#import <Foundation/Foundation.h>
@interface PersonRecord : NSObject
@property (nonatomic, copy) NSString *compositeName;
@property (nonatomic, copy) NSString *organization;
@property (nonatomic, copy) NSString *jobTitle;
@property (nonatomic, copy) NSArray *emails; // of MultiValues
@property (nonatomic, copy) NSArray *phones; // of MultiValues
@end
@interface MultiValue : NSObject
@property (nonatomic, copy) NSString *label;
@property (nonatomic, copy) NSString *value;
@end
#import "PersonRecord.h"
@implementation PersonRecord
@end
@implementation MultiValue
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment