通讯录相关框架详细解析(三)—— Contacts框架基本概览(一)

版本记录

版本号 时间
V1.0 2017.11.12

前言

有关通讯录的框架有好几种,包括AddressBookAddressBookUIContactsContactsUI,这里,iOS9 以后AddressBook、AddressBookUI就不推荐使用了,但是如果要支持ios8以及以前的系统还是要用它们两个框架的。ios9以后系统推荐使用ContactsContactsUI,接下来的几篇我们就详细的解析一下这几个框架。感兴趣的可以看我写的上面几篇。
1. 通讯录相关框架详细解析(一)—— AddressBook框架基本概览
2. 通讯录相关框架详细解析(二)—— AddressBookUI框架基本概览

Contacts框架

1. 基本情况

框架用来访问用户的联系人并格式化本地化联系人信息。

Contacts框架提供了Swift和Objective-C API来访问用户的联系信息。 由于大多数应用程序在不做任何更改的情况下读取联系人信息,因此此框架针对线程安全的只读使用进行了优化。

2. Working with the User’s Contacts - 使用用户联系人

该框架适用于所有Apple平台,并取代iOS和MacOS中的Address Book框架。

Contact Objects

联系人类(CNContact)是联系人属性(如联系人姓名,图像或电话号码)的线程安全的不可变值对象。

contact类就像NSDictionary一样,它有一个可修改的子类CNMutableContact,可以用来修改联系人属性。 对于可以具有多个值(例如电话号码或电子邮件地址)的联系人属性,将使用CNLabeledValue对象数组。 带标签的value类是线程安全的,不可变的标签和值的元组。 标签向用户描述每个值,允许差异化,例如家庭和工作电话号码。 Contacts框架提供了一些预定义的标签,您可以创建自己的自定义标签。

// Listing 1 Creating a contact 

// import Contacts
 
// Creating a mutable object to add to the contact
let contact = CNMutableContact()
 
contact.imageData = NSData() // The profile picture as a NSData object
 
contact.givenName = "John"
contact.familyName = "Appleseed"
 
let homeEmail = CNLabeledValue(label:CNLabelHome, value:"john@example.com")
let workEmail = CNLabeledValue(label:CNLabelWork, value:"j.appleseed@icloud.com")
contact.emailAddresses = [homeEmail, workEmail]
 
contact.phoneNumbers = [CNLabeledValue(
    label:CNLabelPhoneNumberiPhone,
    value:CNPhoneNumber(stringValue:"(408) 555-0126"))]
 
let homeAddress = CNMutablePostalAddress()
homeAddress.street = "1 Infinite Loop"
homeAddress.city = "Cupertino"
homeAddress.state = "CA"
homeAddress.postalCode = "95014"
contact.postalAddresses = [CNLabeledValue(label:CNLabelHome, value:homeAddress)]
 
let birthday = NSDateComponents()
birthday.day = 1
birthday.month = 4
birthday.year = 1988  // You can omit the year value for a yearless birthday
contact.birthday = birthday
 
// Saving the newly created contact
let store = CNContactStore()
let saveRequest = CNSaveRequest()
saveRequest.addContact(contact, toContainerWithIdentifier:nil)
try! store.executeSaveRequest(saveRequest)

Formatting and Localization

Contacts框架可帮助您格式化和本地化联系人信息。 例如,您可以正确格式化联系人姓名(使用
CNContactFormatter)或格式化国际邮政地址(使用 CNPostalAddressFormatter)。

// Listing 2  Formatting a name and postal address
// Formatting contact name

let fullName = CNContactFormatter.stringFromContact(contact, style: .FullName)
print(fullName)
// John Appleseed
 
// Formatting postal address
let postalString = CNPostalAddressFormatter.stringFromPostalAddress(homeAddress)
print(postalString)
// 1 Infinite Loop
// Cupertino
// CA
// 95014

您可以根据设备的当前区域设置显示本地化的对象属性名称和预定义的标签。Contacts框架中定义的许多对象(例如CNContact)都包含localizedStringForKey:方法,该方法使您可以获取Key名称的本地化版本。 另外,CNLabeledValue类还包括了localizedStringForLabel:方法,它可以让你获得Contacts框架中预定义标签的本地化标签。

// Listing 3  Localizing a given name

// device locale is Spanish

let displayName = CNContact.localizedStringForKey(CNContactNicknameKey)
print(displayName)
// alias 
let displayLabel = CNLabeledValue.localizedStringForLabel(CNLabelHome)
print(displayLabel)
// casa

Fetching Contacts

您可以使用代表用户的联系人数据库的联系人存储(CNContactStore)来获取联系人。 contact store封装了所有的I / O操作,负责获取和保存联系人和组。 由于contact store方法是同步的,因此建议您在后台线程上使用它们。 如果需要,您可以安全地将不可变的提取结果发送回主线程。

Contacts框架提供了几种方法来限制从提取返回的联系人,包括预定义的谓词和keysToFetch属性。

CNContact提供了用于过滤要获取的联系人的谓词。 例如,要获取名称为“Appleseed”的联系人,请使用predicateForContactsMatchingName:并传入Appleseed

// Listing 4 Fetching contacts using predicates

let predicate: NSPredicate = CNContact.predicateForContactsMatchingName("Appleseed")

请注意,通用框架和复合谓词不被Contacts框架支持。

您可以使用keysToFetch来限制提取的联系人属性。 例如,如果只想获取联系人的给定名称和姓氏,则可以在keysToFetch数组中指定这些联系人键。

// Listing 5  Fetching contacts using KeysToFetch

let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey]

要使用谓词(predicateForContactsMatchingName :)keysToFetch数组,请使用unifiedContactsMatchingPredicate:keysToFetch:error :获取联系人。

// Listing 6  Fetching contacts using a predicate and an array of contact attributes

let store = CNContactStore()
let contacts = try store.unifiedContactsMatchingPredicate(CNContact.predicateForContactsMatchingName("Appleseed"), keysToFetch:[CNContactGivenNameKey, CNContactFamilyNameKey])

Contacts框架还可以对提取的联系人执行操作,例如格式化联系人姓名。 每个操作都需要一组特定的联系人键才能正确执行操作。 这些键被指定为必须包含在keysToFetch数组中的键描述符对象。 例如,如果您想要获取联系人的电子邮件地址,并且能够格式化联系人的名称(使用CNContactFormatter),请在keysToFetch数组中包含CNContactEmailAddressesKeydescriptorForRequiredKeysForStyle返回的关键描述符对象。

// Listing 7 Fetching contacts with key descriptors

let keysToFetch = [CNContactEmailAddressesKey, CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName)]

Privacy

用户可以根据每个应用授予或拒绝访问联系人数据。 任何对CNContactStore的调用都会在用户被要求授予或拒绝访问的同时阻止应用程序。 请注意,仅在第一次请求访问时提示用户; 任何后续的CNContactStore调用都使用现有的权限。 为了避免让你的应用程序的UI主线程阻塞这个访问,可以使用异步方法requestAccessForEntityType:completionHandler:或者将CNContactStore用法分派到后台线程。

重要:在iOS 10.0或之后链接的iOS应用必须在其Info.plist文件中包含它需要访问的数据类型的使用说明密钥,否则将会崩溃。 要特别访问联系人数据,它必须包含NSContactsUsageDescription

Partial Contacts

部分联系人只有部分属性值从contact store中获取。 所有提取的联系人对象都是部分联系人。 如果您尝试访问未提取的属性值,则会发生异常。 如果您不确定在联系人中提取了哪些密钥,建议您在访问属性值之前检查属性值的可用性。 您可以使用isKeyAvailable:检查单个联系人密钥的可用性,或者是可用可用:检查多个密钥。 如果所需的按键不可用,则重新与他们联系。

// Listing 8  Checking the availability of a key

// Checking if phone number is available for the given contact.

if (contact.isKeyAvailable(CNContactPhoneNumbersKey)) {
    print("\(contact.phoneNumbers)")
} else {
    //Refetch the keys
    let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPhoneNumbersKey]
    let refetchedContact = try store.unifiedContactWithIdentifier(contact.identifier, keysToFetch: keysToFetch)
    print("\(refetchedContact.phoneNumbers)")
}

Unified Contacts

代表同一个人的不同账户中的联系人可以自动链接在一起。 链接的联系人在macOS和iOS应用程序中显示为统一联系人。 统一联系人是合并到一个联系人中的一组链接联系人的内存中暂时视图。

默认情况下Contacts框架返回统一联系人。 每个获取的统一联系人(CNContact)对象都具有其自己的唯一标识符,该标识符与链接联系人组中的任何个人联系人标识符不同。 统一联系人的重新提取应该使用其标识符来完成。

Saving Contacts

联系人存储(CNContactStore)也用于保存对Contacts框架对象的更改。 CNSaveRequest类启用保存操作,并允许将多个联系人和组的更改批量分配到一个操作中。 将所有对象添加到保存请求后,可以使用contact store执行该对象,如Listing 9所示,代码清单如下所示。 执行保存时不要访问保存请求中的对象,因为对象可能会被修改。

注意:CNSaveRequest在watchOS中不可用。

// Listing 9 Saving a new contact

// Creating a new contact

let newContact = CNMutableContact()
newContact.givenName = "John"
newContact.familyName = "Appleseed"
 
// Saving contact
let saveRequest = CNSaveRequest()
saveRequest.addContact(newContact, toContainerWithIdentifier:nil)
try! store.executeSaveRequest(saveRequest)
// Listing 10  Saving a modified contact

let mutableContact = contact.mutableCopy() as! CNMutableContact
let newEmail = CNLabeledValue(label: CNLabelHome, value: "john@example.com")
mutableContact.emailAddresses.append(newEmail)
 
let saveRequest = CNSaveRequest()
saveRequest.updateContact(mutableContact)
try! store.executeSaveRequest(saveRequest)

Contacts Changed Notifications

保存成功执行后, contact store会将CNContactStoreDidChangeNotification通知发送到默认通知中心。 如果您缓存任何Contacts框架对象,您需要重新获取这些对象,或者通过它们的标识符,或用于最初获取它们的谓词,然后释放缓存的对象。 请注意,缓存的对象是陈旧的,但不是无效的。

Containers and Groups

用户可能在其设备的本地帐户或配置为同步联系人的服务器帐户中有联系人。 每个帐户至少有一个联系人的容器。 联系人只能在一个容器中。

Figure 3

一个组是一个容器内的一组联系人。 并非所有帐户都支持群组,某些帐户支持子群组。 一个iCloud帐户只有一个容器,可能有许多组,但没有子组。 另一方面,Exchange帐户不支持组,但可能有多个表示Exchange文件夹的容器。

Figure 4

Contacts框架基本结构

下面我们就看一下Contacts框架的基本结构。

1. Classes

  • CNContact

    • 表示联系人属性的不可变值对象的线程安全类,例如联系人的名字和电话号码。
  • CNContactFetchRequest

    • 定义获取联系人时使用的提取选项的对象。
  • CNContactRelation

    • CNContactRelation类定义了一个不可变的值对象,表示与另一个联系人有关的联系人。 这是一个线程安全的类。
  • CNContactStore

    • CNContactStore类是一个线程安全的类,可以获取并保存联系人,组和容器。
  • CNContactsUserDefaults

    • CNContactsUserDefaults类定义用于访问联系人的用户默认值的属性。
  • CNContainer
    • 一个线程安全的类,它定义了一个表示容器的不可变对象。
  • CNGroup
    • 定义表示组的不可变对象的线程安全类。
  • CNLabeledValue
    • 一个线程安全的类,它定义了一个将联系人属性值与标签结合在一起的不可变值对象,比如与Home,Work或iPhone标签结合的联系人电话号码。
  • CNMutableContact
    • 联系人属性的可变值对象,例如联系人的名字和电话号码。
  • CNPhoneNumber
    • 一个线程安全的类,它定义了一个代表联系人电话号码的不可变值对象。
  • CNSocialProfile
    • 一个线程安全的类,它定义了一个代表社交信息的不可变对象。

2. Reference

后记

未完,待续~~~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容