iOS 通讯录 MFContacts

iOS通讯录的访问只在一些特定类别的APP中会有所涉及,例如通讯类、社交类、云备份类等,iOS 9.0以前Apple公司提供给开发者的为AddressBook.framework,9.0以及之后放弃了AddressBook.framework,新推出了Contacts.framework,目前iOS平台的大部分APP对系统版本的支持还存在iOS 8.0甚至到7.0的支持,因此对通讯录的访问功能基本上要同时接入这两个框架的代码。下面简单总结对比了下两个框架的特点,后面介绍了针对这两个框架封装出一个统一的调用API的通讯录访问的库MFContacts,实现了基本的增删改查功能,对支持多版本iOS的兼容性问题有了一个很好的解决。

AddressBook.framework

  1. API接口以C语言的形式直接暴露给开发者调用,对象都是以句柄来操作,需要注意内存上的把握,在适当时机Release你申请或者创建的资源。
  2. 接口功能划分与调用方式非常分散,联系人的各项属性配置相当繁杂,不友好。
  3. 没有类似于照片库中的资源id,通讯录中的联系人没有可以完全标识其唯一性的id属性,只有整数形式的RecordID字段,官方建议的是用RecordID+Name本地化后来标识。
  4. 直接使用其API不能保证线程安全,官方建议开发者在多线程间共享同一个AddressBook实例,将对其的查询、修改、写入等操作序列化到一个串行队列中执行,以保证线程案例。
  5. 对通讯录内的数据做的更改,仅仅是APP内的本地操作,官方建议每做一些更改操作应该尽快更新到iOS系统内的通讯录数据库中,防止更改丢失。

框架中的AddressBook.h文件注释中给出的一些建议

    /*
     *  This library is not threadsafe. Recommended practice is to use
     *  a separate ABAddressBook instance on each thread in your
     *  application. You can register a callback on each instance
     *  that will be invoked when another instance in another thread
     *  or process makes changes to the shared AddressBook database.
     *  
     *  Alternately if needed, multiple threads could use a shared 
     *  ABAddressBook instance by serializing all AddressBook API usage 
     *  with the same dispatch queue. It is important to follow these 
     *  rules when doing this:
     *  1) Threads can only use value types from the block's
     *     ABRecordRef and ABMultiValueRef objects.
     *  2) Threads can reference these objects by their ABRecordID
     *     and ABMultiValueIdentifier.
     *  3) The AddressBook change callback must also be serialized
     *     with the same dispatch queue. For example, to revert
     *     the ABAddressBook instance.
     *
     *  Changes to records have no external effect until the ABAddressBook
     *  is saved. It is possible for other ABAddressBook instances to make
     *  conflicting changes during that time. To reduce the likelihood
     *  of that event, save the ABAddressBook as soon as possible after
     *  each set of related changes.
     *
     */

Contacts.framework

  1. API接口以Objective-C的形式封装给开发者调用。
  2. 接口功能划分与调用方式非常完整清晰,联系人的主要属性都以对象的形式封装起来并且统一标签化,更方便调用。
  3. 联系人提供了identifier属性,以标识当前设备中通讯录内的唯一性,方便查询管理。
  4. 在读取系统联系人信息时可以有选择性的读取感兴趣的字段,相比默认全部载入,效率更高。
  5. 提供联系人、地址、电话等信息的格式化功能,方便UI展现。

框架中的CNContact.h与CNContactStore.h文件注释中给出的一些建议

    /*!
     * @abstract An immutable value object representing a contact.
     *
     * @discussion CNContact is thread safe. 
     *
     * If using a CNContact instance where you are not certain of the keys that were fetched, use isKeyAvailable: or areKeysAvailable:. If these return NO you need to refetch the contact by the contact identifier with the keys you want to fetch. Accessing a property that was not fetched will throw CNContactPropertyNotFetchedExceptionName.
     */

    /*!
     * @abstract Provides methods to fetch and save contacts.
     *
     * @discussion The CNContactStore is a thread safe class that can fetch and save contacts, fetch and save groups, and fetch containers.
     *
     * @note Some good practices are:
     * 1) Only fetch contact properties that will be used.
     * 2) When fetching all contacts and caching the results, first fetch all contact identifiers only. Then fetch batches of detailed contacts by identifiers as you need them.
     * 3) To aggregate several contact fetches collect a set of unique contact identifiers from the fetches. Then fetch batches of detailed contacts by identifiers.
     * 4) When CNContactStoreDidChangeNotification is posted, if you cache any fetched contacts/groups/containers then they must be refetched and the old cached objects released.
     */

MFContacts

为了方便应用层调用,我基于两个通讯录框架的基础API,构建了一套MFContacts库,向应用层暴露统一的调用接口,将不同Framework API的差异性限制在MFContacts库内,应用层访问系统通讯录时甚至不知道实际使用的是AddressBook.framework还是Contacts.framework。
MFContacts由上至下总共分为三层:

封装层

以MFContactsHelperProtocol为统一接口,使AddressBook.framework与Contacts.framework两个库的功能向外保持一致的调用方式,同时所有的操作保持在一个线程队列中顺序执行,以保证线程安全。最外层MFContactsManager基于两个Helper实例,向外提供无差异化的通讯录操作。

调度层

调度层三个Routine统一继承自BaseRoutine,封装了以通讯录为单位的读取写入操作,访问权限申请与查询、内容变更监听功能。

转换层

由三个对象构成,Buider对象通过Composer与Extractor对象完成原始联系人信息的提取和写入,向调度层提供联系人为单位的构建功能,Composer与Extractor完成系统Model属性与封装层Model属性的相互转换功能。

iOS 通讯录 MFContacts_第1张图片
Contacts Components.png

demo代码地址
download

参考资料
Address Book Programming Guide for iOS
AddressBook Framework
Contacs Framework

你可能感兴趣的:(iOS 通讯录 MFContacts)