前言

iOS通讯录框架有AddressBook,AddressBookUI,Contacts,ContactsUI.
iOS9 AddressBook,AddressBookUI不推荐使用了,用Contacts,ContactsUI代替。
如果项目支持到iOS8或以下,还是使用AddressBook,AddressBookUI。本篇只涉及AddressBook框架的4个类。
可以看到AddressBook包含内容如下图

授权

首先导入头文件 #import <AddressBook/AddressBook.h>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//如果用户没决定过,请求授权
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
//创建通讯录
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
//请求授权
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {//请求授权页面用户同意授权
//读取通讯录人员数量,此处不可使用上面请求授权的通讯录对象,会崩溃
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
//获取联系人数量
CFIndex personCount = ABAddressBookGetPersonCount(addressBook);
dispatch_async(dispatch_get_main_queue(), ^{
//xxx
});
CFRelease(addressBook);
});
CFRelease(addressBookRef);
//如果是已授权状态
} else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
//创建通讯录
//读取通讯录人员数量
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, NULL);
CFIndex personCount = ABAddressBookGetPersonCount(addressBookRef);
//xxx
CFRelease(addressBookRef);
} else {
[self showAlertWithTitle:@"提醒" message:@"请在设置中打开通讯录授权"];
}

授权状态一共有四个值,可以自己点击去头文件看一下。为什么release,CoreFoundation框架内存需要手动管理,遇到create,copy,new,retain都需要release。

读取联系人信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
//拿到所有联系人
CFArrayRef peopleArray = ABAddressBookCopyArrayOfAllPeople(addressBook);
//数组数量
CFIndex peopleCount = CFArrayGetCount(peopleArray);
for (int i = 0; i < peopleCount; i++) {
//拿到一个人
ABRecordRef person = CFArrayGetValueAtIndex(peopleArray, i);
//拿到姓名
//姓
NSString *lastNameValue = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonLastNameProperty);
//名
NSString *firstNameValue = (__bridge_transfer NSString *)ABRecordCopyValue(person, kABPersonFirstNameProperty);

//拿到多值电话
ABMultiValueRef phones = ABRecordCopyValue(person, kABPersonPhoneProperty);
//多值数量
CFIndex phoneCount = ABMultiValueGetCount(phones);
for (int j = 0; j < phoneCount ; j++) {
//电话标签本地化(例如是住宅,工作等)
NSString *phoneLabel = (__bridge_transfer NSString *)ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(phones, j));
//拿到标签下对应的电话号码
NSString *phoneValue = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(phones, j);
}
CFRelease(phones);

//邮箱多值
ABMultiValueRef emails = ABRecordCopyValue(person, kABPersonEmailProperty);
CFIndex emailCount = ABMultiValueGetCount(emails);
for (int k = 0; k < emailCount; k++) {
NSString *emailLabel = (__bridge_transfer NSString *)ABAddressBookCopyLocalizedLabel(ABMultiValueCopyLabelAtIndex(emails, k));
NSString *emailValue = (__bridge_transfer NSString *)ABMultiValueCopyValueAtIndex(emails, k);
}
CFRelease(emails);
}
CFRelease(addressBook);
CFRelease(peopleArray);

关于__bridge_transfer,是桥接的关键字之一,还有__bridge_retained和__bridge,可以自行搜索桥接了解,关键的是转换对象所有权的问题。

新增联系人
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
ABAddressBookRef addressBook = ABAddressBookCreateWithOptions(NULL, NULL);
for (IABPerson *iPerson in array) {
//创建一个联系人
ABRecordRef person = ABPersonCreate();
//新增姓名
NSString *Name = iPerson.Name;
//转换为CFString
CFStringRef lastName = (__bridge_retained CFStringRef)Name;
//设置属性
ABRecordSetValue(person, kABPersonLastNameProperty, lastName, NULL);
CFRelease(lastName);
//新增电话
ABMultiValueRef phones = ABMultiValueCreateMutable(kABMultiStringPropertyType);
//手机标签设置值
CFStringRef mobile = (__bridge_retained CFStringRef)iPerson.MobilePhone;
ABMultiValueAddValueAndLabel(phones, mobile, kABPersonPhoneMobileLabel, NULL);
CFRelease(mobile);
//住宅标签设置值
CFStringRef homeTel = (__bridge_retained CFStringRef)iPerson.HomeTel;
ABMultiValueAddValueAndLabel(phones, homeTel, kABHomeLabel, NULL);
CFRelease(homeTel);
//工作标签设置值
CFStringRef workTel = (__bridge_retained CFStringRef)iPerson.WorkTel;
ABMultiValueAddValueAndLabel(phones, workTel, kABWorkLabel, NULL);
CFRelease(workTel);
//其他标签设置值
CFStringRef otherTel = (__bridge_retained CFStringRef)iPerson.OtherTel;
ABMultiValueAddValueAndLabel(phones, otherTel, kABOtherLabel, NULL);
CFRelease(otherTel);
//为联系人的电话多值 设置值
ABRecordSetValue(person, kABPersonPhoneProperty, phones, NULL);

//新增邮箱
ABMultiValueRef emails = ABMultiValueCreateMutable(kABPersonEmailProperty);
//住宅邮箱设置值
CFStringRef email = (__bridge_retained CFStringRef)iPerson.Email;
ABMultiValueAddValueAndLabel(emails, email, kABHomeLabel, NULL);
CFRelease(email);
//为联系人添加邮箱多值
ABRecordSetValue(person, kABPersonEmailProperty, emails, NULL);
//给通讯录添加联系人
ABAddressBookAddRecord(addressBook, person, NULL);
CFRelease(person);
CFRelease(phones);
CFRelease(emails);
}
//保存通讯录,一定要保存
ABAddressBookSave(addressBook, NULL);
CFRelease(addressBook);

这里有一点就是,电话下面的标签,如果你在电话属性下面找,有住宅,工作,其他,但都是传真。其实是在通用下面,因为邮箱也用得到这些标签。