Swift - 使用Contacts访问通讯录1(纯代码获取联系人)
作者:hangge | 2017-02-06 08:10
在 iOS9.0 之前, 我们只能通过 AddressBook 框架来获取通讯录联系人信息。但 AddressBook framework 语法很奇怪,同时也十分难用。所以苹果从 iOS9.0 开始推出的全新的联系人框架 Contacts FrameWork 作为替代,同时将原来的 AddressBook 给废弃掉。
Contacts FrameWork 同样包含两种访问通讯录的方式:
前文中我介绍了 ContactsUI.framework 的使用,本文接着演示 Contacts.framework 框架的使用。
Contacts FrameWork 同样包含两种访问通讯录的方式:
- ContactsUI.framework 框架 : 通过系统提供的通讯录交互界面来访问(替代原先的 AddressBookUI.framework)
- Contacts.framework 框架 : 没有界面,通过代码来获取所有联系人信息(替代原先的 AddressBook.framework)
一、Contacts.framework介绍
1,Contacts使用步骤
(1)获取用户的授权,如果授权则往下继续进行。
(2)创建通讯录对象
(3)创建联系人请求对象
(4)遍历所有联系人,获取联系人信息
2,联系人记录的属性
每一条联系人记录中,都有很多属性,而这些属性又分为单值属性和多值属性。
(1)单值属性是只有一个值的属性:
- familyName :姓
- givenName :名
- middleName :中间名
- previousFamilyName :前缀
- nameSuffix :后缀
- phoneticFamilyName :姓氏汉语拼音或音标
- phoneticGivenName :名字汉语拼音或音标
- nickname :昵称
- organizationName :公司(组织)
- jobTitle :职位
- departmentName :部门
- note :备注
(2)多值属性是包含多个值的集合类型:
- phoneNumbers :电话
- emailAddresses :Email
- postalAddresses :地址
- urlAddresses :URL属性
- dates :纪念日
- instantMessageAddresses :获取即时通讯(IM)
- socialProfiles :社交账号
- contactRelations :亲属关系人
3,多值属性标签的本地名称
在多值属性中包含了 label(标签)、value(值)和 ID 等部分,其中标签和值都是可以重复的,而 ID 是不能重复的。对于标签,我们可以使用 CNLabeledValue<NSString>.localizedString(forLabel:) 方法转为本地标签名(即能看得懂的标签名,比如 work、home)。要不然打印出来的是 _$!<Home>!$_,_$!<Work>!$_ 这样的数据。
二、获取所有联系人信息
1,效果图
(1)程序启动后自动开始请求并获取通讯录信息。
(2)同时将通讯录里的所有联系人信息输出到控制台中。(具体包括联系人的姓名、昵称、公司、职位、部门、备注、电话、email、 地址、纪念日、即时通讯)

2,Info.plist配置
由于苹果安全策略更新,在使用 Xcode8 开发时,需要在 Info.plist 配置请求通讯录的相关描述字段(Privacy - Contacts Usage Description)
3,样例代码
import UIKit
import Contacts
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
CNContactStore().requestAccess(for: .contacts) { (isRight, error) in
if isRight {
//授权成功加载数据。
self.loadContactsData()
}
}
}
func loadContactsData() {
//获取授权状态
let status = CNContactStore.authorizationStatus(for: .contacts)
//判断当前授权状态
guard status == .authorized else { return }
//创建通讯录对象
let store = CNContactStore()
//获取Fetch,并且指定要获取联系人中的什么属性
let keys = [CNContactFamilyNameKey, CNContactGivenNameKey, CNContactNicknameKey,
CNContactOrganizationNameKey, CNContactJobTitleKey,
CNContactDepartmentNameKey, CNContactNoteKey, CNContactPhoneNumbersKey,
CNContactEmailAddressesKey, CNContactPostalAddressesKey,
CNContactDatesKey, CNContactInstantMessageAddressesKey
]
//创建请求对象
//需要传入一个(keysToFetch: [CNKeyDescriptor]) 包含CNKeyDescriptor类型的数组
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
//遍历所有联系人
do {
try store.enumerateContacts(with: request, usingBlock: {
(contact : CNContact, stop : UnsafeMutablePointer<ObjCBool>) -> Void in
//获取姓名
let lastName = contact.familyName
let firstName = contact.givenName
print("姓名:\(lastName)\(firstName)")
//获取昵称
let nikeName = contact.nickname
print("昵称:\(nikeName)")
//获取公司(组织)
let organization = contact.organizationName
print("公司(组织):\(organization)")
//获取职位
let jobTitle = contact.jobTitle
print("职位:\(jobTitle)")
//获取部门
let department = contact.departmentName
print("部门:\(department)")
//获取备注
let note = contact.note
print("备注:\(note)")
//获取电话号码
print("电话:")
for phone in contact.phoneNumbers {
//获得标签名(转为能看得懂的本地标签名,比如work、home)
var label = "未知标签"
if phone.label != nil {
label = CNLabeledValue<NSString>.localizedString(forLabel:
phone.label!)
}
//获取号码
let value = phone.value.stringValue
print("\t\(label):\(value)")
}
//获取Email
print("Email:")
for email in contact.emailAddresses {
//获得标签名(转为能看得懂的本地标签名)
var label = "未知标签"
if email.label != nil {
label = CNLabeledValue<NSString>.localizedString(forLabel:
email.label!)
}
//获取值
let value = email.value
print("\t\(label):\(value)")
}
//获取地址
print("地址:")
for address in contact.postalAddresses {
//获得标签名(转为能看得懂的本地标签名)
var label = "未知标签"
if address.label != nil {
label = CNLabeledValue<NSString>.localizedString(forLabel:
address.label!)
}
//获取值
let detail = address.value
let contry = detail.value(forKey: CNPostalAddressCountryKey) ?? ""
let state = detail.value(forKey: CNPostalAddressStateKey) ?? ""
let city = detail.value(forKey: CNPostalAddressCityKey) ?? ""
let street = detail.value(forKey: CNPostalAddressStreetKey) ?? ""
let code = detail.value(forKey: CNPostalAddressPostalCodeKey) ?? ""
let str = "国家:\(contry) 省:\(state) 城市:\(city) 街道:\(street)"
+ " 邮编:\(code)"
print("\t\(label):\(str)")
}
//获取纪念日
print("纪念日:")
for date in contact.dates {
//获得标签名(转为能看得懂的本地标签名)
var label = "未知标签"
if date.label != nil {
label = CNLabeledValue<NSString>.localizedString(forLabel:
date.label!)
}
//获取值
let dateComponents = date.value as DateComponents
let value = NSCalendar.current.date(from: dateComponents)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy年MM月dd日 HH:mm:ss"
print("\t\(label):\(dateFormatter.string(from: value!))")
}
//获取即时通讯(IM)
print("即时通讯(IM):")
for im in contact.instantMessageAddresses {
//获得标签名(转为能看得懂的本地标签名)
var label = "未知标签"
if im.label != nil {
label = CNLabeledValue<NSString>.localizedString(forLabel:
im.label!)
}
//获取值
let detail = im.value
let username = detail.value(forKey: CNInstantMessageAddressUsernameKey)
?? ""
let service = detail.value(forKey: CNInstantMessageAddressServiceKey)
?? ""
print("\t\(label):\(username) 服务:\(service)")
}
print("----------------")
})
} catch {
print(error)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
全部评论(3)
怎么判断如果用户请求权限的时候点了不允许然后执行相应操作啊
站长回复:在CNContactStore().requestAccess回调中可以判断是否点了不允许:
打印途中崩溃了兄弟,有输出,但是到了一定数量后就报 拆包可选值时为nil 的错了
站长回复:多谢提醒,代码现已修正,你可以再看下。
亲,为什么这种方式获取的联系人数据不完整呢,比如说有些明明存储了的,但是却读取不到
站长回复:我又真机测试了下,没有碰到读取不到的情况,不清楚你那边是什么问题了。