Mac OS X地址簿编程指南

Address Book Programming Guide for Mac OS X

一、简介

地址簿是一种围绕联系人和组信息中心数据、可以查看这些信息应用程序、以及在程序中访问这些信息的编程接口的技术。数据库包含了诸如用户名、街道、email地址、电话号码和分发列表等信息。使用地址簿框架的地址簿可以和其他程序共享这个联系人信息,包括Mail和iChat,或者对其进行扩展以包括应用相关的信息。

地址簿框架提供了两类API:一个是Objective-C的,一个是C的。这两个具有相同的功能,但本文档中的主要代码示例只使用Objective-C。在可能的地方,文档将分析这两种API的区别。使用C编程接口的开发者可以参考“Using the Address Book C API”Address Book C Framework Reference for Mac OS X,获取关于如何将Objective-C代码映射到C。

1、何人应该阅读该文档?

本文档是为那些打算在自己的应用中使用Mac OS X地址簿技术人员设计的。通过它,你可以了解如何访问用户的地址簿,了解地址簿数据库的新属性,并创为地址簿应用程序创建行动插件。

希望你已经熟悉了Xcode和Mac OS X应用开发的基础。

注意:使用iOS上的地址簿技术的开发人员应该了解这种结束的编程接口与Mac OS X上的是不同的。

2、文档的组织

该文档包括以下文章:

  • “关于地址簿”描述了地址簿数据库是什么,以及你如何操作它。
  • “管理地址簿记录”描述了如何添加和删除人员和组,如何将人员安排进组,以及如何为登录的用户找到一个记录。
  • “访问地址簿记录”描述了如何访问个人和组记录中的数据。
  • “搜索地址簿”描述了如何在用户地址簿上执行搜索。
  • “使用地址簿组作为分发列表”描述了如何建立一个组,以使你能够使用它作为一个邮件发送列表,或者其他类型的分发列表。
  • “创建和使用地址行动插件”描述了如何创建行动产检,允许用户在地址簿用用程序中所看到的地址簿数据上执行自定义操作。
  • “导入和导出地址簿人员和组”描述了如何通过使用vCard标准导入和导出人员记录。
  • “使用地址簿C API”包含了使用地址簿C API的特定信息。

3、其他参考

  • 《同步服务编程指南》讨论了内建与Mac OS X中的数据同步引擎。
  • 《身份服务编程指南》讨论了在本地系统上管理用户组的方法,包括标准的登录帐户和共享帐户。


二、关于地址簿

地址簿框架为人员的联系人和其他个人信息使用了中心数据库。用户仅需要登入该信息一次,而不用在任何需要使用它时反复的登录。支持地址簿框架的应用程序可以和其他应用共享联系人信息,包括苹果的Mail和iChat。计算机上的每个用户都有且仅有一个地址簿。每个应用程序都为当前登录的用户共享地址簿。

1、地址簿如何处理个人和组

地址簿框架支持两个基本类型的记录:ABPerson,用于个人;ABGroup,用于组。两者都是相同根类,ABRecord的子类,在某些场合两者可以互换。

一条ABPerson记录包含了诸如人员姓名、公司、地址、email地址、电话号码、即时消息ID、以及备注等属性。

一个ABGroup对象可以包含任意数量的人员和其他组;一个人员可以被放入任意多数量的组。例如,假设你是为两个公司工作的顾问,Acme Co. 和Ajax Inc.。你可以建立一个Acme雇员组以及一个Ajax雇员组,并让每个公司的雇员是各自组中的成员。然后你可以建立一个Professionals组,包含Acme组和Ajax组,以及其他一些同时属于这两个组的人员。

另外,组和人员记录有以下特点:

  • 每个组合人员都有唯一标识符。当记录被创建时它就设置了,确保即使用户修改了组或人员名称或其他信息时也不会被改变。如果你的应用程序需要存储对一个组或人员的引用,就可以使用该标识符。更多信息可以参见ABRecord的方法uniqueId。
  • 组合人员被存储在一个可延长的表格中。这样,你就可以添加自定义属性到地址表记录中,其他应用会忽略它,无需担心数据奔溃或不稳定问题。更多信息参见“Adding Properties to Address Book Records.”
  • 某些属性可以包含多个值。例如,一个人员可以有任意多的街道地址、电话号码,以及email地址。更多信息参见“Using Multivalue Lists.”

2、地址簿如何管理个人搜索查询

地址簿框架使用ABSearchElement对象来管理个人搜索查询,该对象可以使用ABGroup和ABPerson的类方法来创建。这里有一个很重要的暗示——因为搜索对象使用这些特定的类创建,ABRecord的自定义子类不包含创建该对象的所需的方法。因此,建议你不要实现ABRecord子类。

关于地址簿记录的更多信息,参加“Searching an Address Book.”

3、其他功能

地址簿框架:

  • 提供透明的记录锁定。如果两个应用程序同时尝试修改一条记录中的同一个属性,那么最后保存修改的应用程序将会成功。数据库不会崩溃。如果两个应用程序修改同一条记录的不同属性,那么这两个修改都会成功。
  • 不会提供超出Mac OS X所提供的安全之外的任何安全。任何拥有读写某用户home文件夹权限的人都可以读写该用户的地址簿。正因如此,地址簿不是一个适合存放机密信息的地方,例如信用卡号。
  • 提供本地版本的内建属性名和标签。如果你添加属性或表见,你必须为本地化他们提供自己的方式。
  • 使用同步服务同步它的记录。地址簿框架使用同步服务同步存储在默认属性中的数据;应用程序不应该尝试同步该数据。如果你的应用依赖于自定义的属性来同步,就必须同步他们并且可以要使用同步服务来实现它。参见“添加属性到地址簿记录”。

重要:尝试使用同步服务同步部分地址簿数据库,而不是自定义属性是不可预测的事,并可以会导致数据丢失。


三、管理地址簿记录

可以在用户地址簿中管理人员和组。这一节将解释如何获取用户地址簿、从地址簿中添加和删除人员和组,寻找对应于登录用户的记录以及保存更改。

1、访问地址簿

有两种复制地址簿的方法。较好的方式是使用ABAddressBook的方法addressBook。它返回的地址簿对象只能被用于创建它的同一个线程中;另一种方式是使用ABPerson的方法initWithAddressBook:。

如果你只是进行一次性的查找和编辑,可以ABAddressBook的方法sharedAddressBook。但是,这个方法可能会引起性能的锐减,特别是在紧凑循环中。

例如,你可以将下述代码:

for (id item in someDataStructure) { 
    ABPerson* person = [[ABPerson alloc] init]; 
    // Populate the person from the item 
} 
[[ABAddressBook sharedAddressBook] save]; 
用下述代码替换,就可以大大提高性能:

ABAddressBook* tempBook = [ABAddressBook addressBook]; 
for (id item in someDataStructure) { 
    ABPerson* person = [[ABPerson alloc] initWithAddressBook:tempBook]; 
    // Populate the person from the item 
} 
[tempBook save]; 
2、添加和删除人员和组记录

ABaddressBook类提供了访问、添加和删除组和人员记录的方法。例如,使用groups方法来获取数据库中所有组记录的一个数组,或者用people方法来获取所有人员记录。

使用如下步骤添加一个新的人员或组记录:

  • 获取地址簿。较好的方法是使用ABAddressBook方法addressBook。
  • 创建人员和组记录。你必须分配和初始化各自的ABPerson或ABGroup对象。较好的初始化函数是initWithAddressBook:。
  • 使用ABAddressBook方法removeRecord:添加记录到地址簿.

要删除人员或组,使用ABAddressBook的方法removeRecord:。

3、管理组

地址簿框架允许你添加人员和子组到组中,也支持找出某个人员或子组所在的所有组。

要从组中添加或删除人员,使用addMember:和removeMember:方法。一条人员记录仅在其已经保存到地址簿后才能被添加到组。要获取某个人员所在的所有组,使用parentGroups方法。

你也可以添加多个组到一个组中。例如,用户有一个称为Pet Lovers的组,包含了组Dog Lovers和Cat Lovers。要添加或删除多个组到一个组中,使用addSubgroup:和removeSubgroup:方法。你不能创建循环。例如,如果Dog Lovers是Pet Lovers的子组,那么Pet Lovers就不能是Dog Lovers的子组,无论是直接的还是简介的。要获取子组所在的所有组的列表,使用parentGroups方法。

要获取组中所有内容类表,使用members和subgroups方法。

4、访问用户记录

当前登入的用户可以指定一条包含自身的记录。这使你的应用程序能够查找姓名、地址或用户的电话号码,因此在填写表格时你可以使用它。要获得登入用户的记录,使用ABAddressBook的方法me。要设置登入用户的记录,使用ABAddressBook的setMe:方法。

5、保存你的更改

当你修改地址簿数据库时,这些更改都在内存中,而不是数据库。除非你保存了这些更改,否则它们是会丢失的。要保存更改到数据库,使用ABAddressBook的方法save或saveAndReturnError:。要测试它们是否是未保存的更改,使用ABAddressBook的方法hasUnsavedChanges。

6、更改通知

如果任何应用程序,包括你自己修改了数据库,地址簿就会提交通知。典型地,你根据这些通知来更新应用程序中所有相关的视图或模型对象。地址簿框架发送两类通知:kABDatabaseChangedNotification指明当前的进程做了修改,kABDatabaseChangedExternallyNotification指明另外一个进程做了修改。使用NSNotificationCenter来注册你感兴趣的通知。注意,这些通知在ABAddressBook类的方法sharedAddressBook或C函数ABGetSharedAddressBook调用之前是不会被发送的。

如果你的应用程序正在使用共享地址簿对象(由sharedAddressBook方法返回),那么更改已经被自动完成并在你收到更改同时已经立刻生效了。非共享地址对象(由addressBook方法返回)通常仅用于短期,不会自动处理更改消息。

7、一个例子

这是一个为当前用户地址簿添加一个名为John Doe人员的Objective-C示例。需要注意的是代码如何访问共享地址簿,以及如何分配一个新的ABPerson对象。还要注意的是,使用的属性(这里,仅仅是名和姓)和最后的保存,它会向用户地址簿发送更改。

ABAddressBook *addressBook; 
ABPerson *newPerson; 
addressBook = [ABAddressBook sharedAddressBook]; 
newPerson = [[[ABPerson alloc] init] autorelease];
[newPerson setValue:@"John" 
        forProperty:kABFirstNameProperty]; 
[newPerson setValue:@"Doe" 
        forProperty:kABLastNameProperty]; 
[addressBook addRecord:newPerson]; 
[addressBook save]; 

四、访问地址簿记录

在有了一条记录后,你一在其上获取数据。本章为你展示了数据是如何组织的,以及如何访问它。它说明了如何来自访问记录属性列表的属性,如何处理多于一个值(例如地址和电话号码)的属性,如何为属性获取本地名,以及如何为人员关联图片。

1、使用属性列表

组和人员都将他们的数据存放在属性列表中。这是你的程序可以添加属性到地址簿记录,而其他应用程序会忽略。参见“Adding Properties to Address Book Records.”

  • 要从记录中获取数据,如组的描述或人员的名,使用valueForProperty:方法,或者是C函数ABRecordCopyValue。例如,获取aPerson的名,可以用:

[aPerson valueForProperty:kABFirstNameProperty];

[aGroup setValue:@"Book Club" forProperty:kABGroupNameProperty];

  • 要查询默认属性的名字,参考表1.

表1 属性列表常量文档

文档语言

“Default Record Properties” inAddress Book Objective-C Constants Reference

ABRecord

Objective-C

“Default Person Properties” inAddress Book Objective-C Constants Reference

ABPerson

Objective-C

“Default Group Properties” inAddress Book Objective-C Constants Reference

ABGroup

Objective-C

“Constants” inABPerson C Reference

ABPersonRefProcedural C

“Constants” inABGroup C Reference

ABGroupRefProcedural C

2、使用多值列表

很多属性都有多个值,例如,一个人员可以有多个地址,包括工作、家庭和邮件地址。这些属性被存储为多值列表,类型是ABMultiValue。多值列表中的每个条目都有一个数字索引,一个唯一标识,一个字符串标签(如Home或Work),以及一个值。多值列表中的每个值都必须是相同类型。标签可以不唯一;毕竟,一些人可以有多个家庭或工作地址。

你可以使用数字索引来访问多值列表中的条目,但这会因为用户的添加和删除值而包含很多更改。如果你希望保存对指定值的引用,请使用唯一标识符,这可以保证不会被更改。你可以在所以和唯一标识符之间来回转换:

每个多值列表都有也个主值,这是用户与该人员关系最为密切的条目。例如,朋友可能有家庭和工作地址,但是家庭地址是他的主要地址。同事可能也有家庭和工作电话号码,但工作号码是他们的主要号码。

3、为人员关联图片

一个人也可以有相关的图片图像。图像并不真的存放在地址簿数据库(一个属性列表)中——它被存放在单独的图像文件中。这意味着你需要使用不同的方法来访问图像数据。你可以使用setImageData:方法来设置图像,或者使用imageData 方法来获取图像。使用NSImage的initWithData: 方法转换通过imageData方法返回的NSDate对象为NSImage对象。

地址簿框架通过特定的搜索价格定位图像,以这个顺序:

(1)检查用户特别设置的图像。

(2)为本地用户的登录图片检查目录服务。

(3)在 /Network/Library/Images/People/email 检查图像,这里的email是用户的主email地址。

(4)从用户的MobileMe帐户检查图片,首先是缓存在~/Library/Caches/com.apple.AddressBook/email下的内容,然后是MobileMe服务器。

图像文件可以是本地的也可以是远程的。本地图像是.../Library/Images/People下的任何图像,以及用户使用地址簿应用程序设置的任何图像。远程图像是存储在网络上的图像。远程图像的下载会花费时间,因此提供了获取远程图像的异步API。

如果图像文件不在本地,而你希望执行异步获取,请使用beginLoadingImageDataForClient:方法。你传递一个实现了ABImageClient方法的客户端对象作为参数到该方法上。beginLoadingImageDataForClient: 方法返回一个图像跟踪号。当获取完成时,consumeImageData:forTag: 消息被发送到你的客户端对象。实现该方法来处理新获取的图像。如果你希望取消异步获取,请使用cancelLoadingImageDataForTag: 类方法。

如果图像文件不在本地,而你希望执行异步获取,请使用beginLoadingImageDataForClient: 方法:

  1. 传递一个实现了ABImageClient协议的客户端对象作为参数给该方法。
  2. beginLoadingImageDataForClient: 方法返回一个图像跟踪号。
  3. 当获取成功时,客户端对象会收到meImageData:forTag: 消息。实现该方法来处理新获取的图像。
  4. 如果想取消异步获取,使用cancelLoadingImageDataForTag: 类方法。

4、获取属性和标签的本地名

你可以为任何列举在Address Book Objective-C Constants Reference. 中的默认属性名和标签查询本地名。ABLocalizedPropertyOrLabelABCopyLocalizedPropertyOrLabel 为用户选定的语言返回位于本地的名字。

你必须为你创建的属性和标签处理名字的本地化;地址簿框架不会为此提供任何特殊支持。

5、一个例子

列表1是以一段获取登录用户主地址国家的Objective-C代码示例。如果国家是空字串,就将其设为USA。

列表1 修改人员的地址

ABPerson *aPerson = [[ABAddressBook sharedAddressBook] me]; 
ABMutableMultiValue *anAddressList = 
    [[aPerson valueForProperty:kABAddressProperty] mutableCopy]; 
NSUInteger primaryIndex = 
    [anAddressList indexForIdentifier:[anAddressList primaryIdentifier]]; 
NSMutableDictionary *anAddress = 
    [[anAddressList valueAtIndex:primaryIndex] mutableCopy]; 
NSString *country = 
    (NSString*) [anAddress objectForKey:kABAddressCountryKey]; 
if ([country isEqualToString:@""]) { 
    [anAddress setObject:@"USA" forKey:kABAddressCountryKey]; 
    [anAddressList replaceValueAtIndex:primaryIndex withValue:anAddress]; 
    [aPerson setValue:anAddressList forProperty:kABAddressProperty]; 
    [[ABAddressBook sharedAddressBook] save]; 
} 
5、使用人员拾取器

。它提供了你的应用程序可以自定义的可搜索、可选择的人员和组列表.

要在Cocoa应用中使用人员拾取器,将其拖入你的窗体的库面板中。如果需要,你可以编程创建一个ABPeoplePickerView的实例。哟两种方法建立人员拾取器视图行为——Interface Builder和编程。

要从Interface Builder建立行为,打开Get Info面板。可以设置的attributes包括视图显示哪一列、是否允许多选、是否允许组选、以及为视图自动保存名字扥。这些更改会立即在Interface Builder中反应出来。使用Interface Builder的Test Interface功能,人员拾取器视图将显示登录用户的地址簿。

要通过编程实现,从你的controller创建一个类型为ABPeoplePickerView类型的outlet,并将其连接到人员拾取器视图。然后使用合适的实例方法来更改拾取器属性。

对于Cocoa应用程序,人员拾取器也提供自动保存数据的方法,因此可以保留过滤器选择和列地位。使用ABPeoplePickerView的autosaveNameautosaveName方法。

使用C API,人员拾取器仅在视窗表单中有效,无法用于自定义的视图。必须使用ABPickerCreate 创建并用ABPickerSetVisibility使之可见,如下:

ABPickerRef peoplePicker = ABPickerCreate(); 
ABPickerSetVisibility(peoplePicker, TRUE); 
6、人员拾取器示例

。使用C API的开发者需要参考ABPicker C Reference 来构造类型的应用,大多数函数有类似的名字。因为这里不使用通知,你需要注册事件句柄来处理视窗的变动。

下述列表展示了如何编程设置人员拾取器的特性。该代码是awakeFromNib方法的典型组成部分。所有这些设置也对Interface Builder中的属性拾取器有效。

// Disallow multiple selections in the name list. 
    [peoplePicker setAllowsMultipleSelection:NO]; 
    // Add the e-mail and telephone properties to the view. 
    // By default, the people picker displays only the 
    // Name column. 
    [peoplePicker addProperty:kABEmailProperty]; 
    [peoplePicker addProperty:kABPhoneProperty]; 
下述列表展示了当用户在人员拾取器中选择一条人员记录时如何注册通知。该代码是awakeFromNib方法的典型组成。

NSNotificationCenter* center; 
    center = [NSNotificationCenter defaultCenter]; 
    // Set up a responder for one of the four available notifications, 
    // in this case to tell us when the selection in the people picker 
    // has changed. 
    [center addObserver:self 
            selector:@selector(recordChanged:) 
            name:ABPeoplePickerNameSelectionDidChangeNotification 
            object:peoplePicker]; 
下述列表展示了如何响应用户选择人员拾取器中的人员记录:

- (void)recordChanged:(NSNotification*)notification { 
    NSArray *array; 
    NSImage *personImage; 
    NSString *personFirstName; 
    NSString *personLastName; 

    array = [peoplePicker selectedRecords]; 
    NSAssert([array count] == 1, 
             @"Picker returned multiple selected records"); 
    ABPerson *person = [array objectAtIndex:0]; 

    personImage = [[NSImage alloc] initWithData:[person imageData]]; 
    personFirstName = [person valueForProperty:kABFirstNameProperty], 
    personLastName = [person valueForProperty:kABLastNameProperty]; 

    /* ...do something with the image and name... */ 
    [personImage release]; 

五、搜索地址簿

你可以快速地搜索用户地址簿,使用任意复杂的准则。例如,你可以搜索所有名为Smith的人员,或者所有工作在Acme并住在San Francisco的人员,或者所有工作在Ajax并住在Seattle的人员。

要执行搜索,你需要在搜索元素中封装准则将其传递给地址簿框架。该框架替你完成搜索,并返回结果。让框架处理搜索比起在你的应用中执行搜索来说,有很大的性能优势,因为框架了解数据库的底层布局,并且它能够相应地优化磁盘访问。

注意:你只能搜索用户本地地址簿数据库,而不能是远程的目录,如CardDAV或Exchange。

1、为一个单独属性创建一个搜索元素

要为人员创建一个搜索元素,使用ABPerson类方法searchElementForProperty:label:key:value:comparison:。要为组创建一个搜索元素,使用ABGroup类方法searchElementForProperty:label:key:value:comparison:

如果你希望搜索有特定属性集的人员或组,就忽略它设置的值,传递nil作为值并,传递kABNotEqual作为比较。要搜索不带有某个属性集的人员或组,传递nil作为值,传递kABEqual作为比较。

2、为多属性创建搜索元素

要联合搜索元素,请使用ABSearchElement类方法searchElementForConjunction:children:。该方法有两个参数:

conjunctionOperator描述了如何联合搜索元素。它可以是kABSearchAnd或kABSearchOr。

children是一个搜索元素的NSArray。搜索元素可以是一个仅指定一个属性的简单元素,或者是指定了多个属性的复杂元素。这可以让你创建任意复杂的搜索元素。你不能将组的搜索元素与人员的搜索元素联合在一起。

3、查找匹配搜索元素的记录

要在地址簿中搜索符合搜索元素的记录,请使用ABAddressBook的方法recordsMatchingSearchElement:,它返回一个记录的NSArray。使用ABSearchElement的方法matchesRecord:来测试匹配某个查询的特定记录。

4、搜索示例

列表1展示了查找姓为Smith的所有人的代码。

列表1 一个简单的搜索

ABAddressBook *AB = [ABAddressBook sharedAddressBook];
ABSearchElement *nameIsSmith =
    [ABPerson searchElementForProperty:kABLastNameProperty
                                 label:nil
                                   key:nil
                                 value:@"Smith"
                            comparison:kABEqualCaseInsensitive];
NSArray *peopleFound =
    [AB recordsMatchingSearchElement:nameIsSmith];
列表2展示了住在San Francisco并工作在Acme,或住在Seattle并工作在Ajax的所有人的代码。注意地址的搜索使用的是KABHomeLabel标签——我们仅仅想知道他们住在我们正在搜索的城市,不想知道他们是否工作在同一城市。

列表2 复杂搜索

ABAddressBook *AB = [ABAddressBook sharedAddressBook];
ABSearchElement *inSF =
    [ABPerson searchElementForProperty:kABAddressProperty
                                 label:kABHomeLabel
                                   key:kABAddressCityKey
                                 value:@"San Francisco"
                            comparison:kABEqualCaseInsensitive];
ABSearchElement *atAcme =
    [ABPerson searchElementForProperty:kABOrganizationProperty
                                 label:nil
                                   key:nil
                                 value:@"Acme"
                            comparison:kABContainsSubStringCaseInsensitive];
ABSearchElement *inSeattle =
    [ABPerson searchElementForProperty:kABAddressProperty
                                 label:kABHomeLabel
                                   key:kABAddressCityKey
                                 value:@"Seattle"
                            comparison:kABEqualCaseInsensitive];
ABSearchElement *atAjax =
    [ABPerson searchElementForProperty:kABOrganizationProperty
                                 label:nil
                                   key:nil
                                 value:@"Ajax"
                            comparison:kABContainsSubStringCaseInsensitive];
ABSearchElement *inSFAndAtAcme =
    [ABSearchElement searchElementForConjunction:kABSearchAnd
                                        children:[NSArray arrayWithObjects:
                                            inSF, atAcme, nil]];
ABSearchElement *inSeattleAndAtAjax =
    [ABSearchElement searchElementForConjunction:kABSearchAnd
                                        children:[NSArray arrayWithObjects:
                                            inSeattle, atAjax, nil]];
ABSearchElement *inSFAndAtAcmeOrInSeattleAndAtAjax =
    [ABSearchElement searchElementForConjunction:kABSearchOr
                                        children:[NSArray arrayWithObjects:
                                            inSFAndAtAcme, inSeattleAndAtAjax, nil]];
NSArray *peopleFound =
    [AB recordsMatchingSearchElement:inSFAndAtAcmeOrInSeattleAndAtAjax];


六、使用地址簿组作为分发列表

一个地址簿的组可以被用于分发列表。例如,假设你在周末主持一个读书讨论俱乐部。你可以使用组来维护所有组中所有人员的列表。对于多值属性,如电话号码和email地址,分发列表可以帮你了解当要发送消息给这个组时,使用哪个值。

通常,用户希望使用标识为primary的值作为分发标识,但是在某些情况下有例外。例如,对于同事,他们的主邮件地址很可能是他们的工作地址。但关于你的周末读书俱乐部的消息应该发送到他们的家庭地址。这可以通过为读书俱乐部组设置分发标识为他们的家庭地址来实现。

要选择组将要使用的多值属性的值,使用ABGroup的方法setDistributionIdentifier:forProperty:person:。每个组都可以为任何一个人员使用一个不同的值。用户也可以从地址簿应用程序编辑它,通过选择Edit菜单下的Edit Distribution List完成。

要获取组所选的多值属性的值,使用ABGroup的方法distributionIdentifierForProperty:person:。如果没有设置分发标识符,该方法将返回多值属性的主标识。如果要么是属性要么是人员为nil,该方法返回nil。如果属性不是多值列表属性,或者如果人员不是组的成员。使用ABMultiValue方法的valueForIdentifier: 从分发标识符获取值。


七、添加属性到地址簿记录

可以为地址簿中的人员和组添加你自己的属性。例如,如果你创建了一个小应用来管理狗狗俱乐部,你可以添加属性到每个人员,一次指定那个人的狗狗的名字和品种。或者如果你正在创建一个应用程序来管理商业联系人,你可以添加一个属性来展开所有会议,并电话呼叫那个人。这些属性都存放在地址簿数据库中。不知道新属性的应用程序不会受到它们的影响,也不会修改它们。

注意:存储在自定义属性中的数据不会通过MobileMe同步。如果你的应用程序依赖于这个在多个计算机上同步的数据,就需要它自己来同步该数据。更多详细信息参见同步服务编程指南。不要同步存储在默认属性中的任何数据。地址簿框架已经同步了该数据,从你的应用程序中尝试同步该数据可能导致数据丢失。

当决定是否添加属性到地址簿记录时,将下面以注意事项牢记在心:

避免机密信息的属性,如信用卡号。地址簿框架不会提供任何超出Mac OS X提供的安全级别。任何可以读写用户home文件夹的人都能够读写该用户的地址簿。

避免地址簿数据库中那些对任何人都没用的属性。如果你希望只为登入用户存放信息,对于Cocoa应用来说可参见NSUserDefaults Class Reference,对于基于C的应用程序来说参见Preferences Utilities Reference

如果你认为一个人可能有多于一个的属性,就使用多值列表。你的新的多值列表拥有与地址簿中其他多值列表同样的能力。用户可以在列表中选择一个主值,并为他创建分发列表。

要为每个人员或组添加属性,使用ABPerson或ABGroup类方法addPropertiesAndTypes:。这些过程带有一个字典,在其中键是新属性的名字,值是它们的类型。注意属性名必须是唯一的。你可能为你的属性使用reverse-DNS风格的名字,要确保没有其他属性使用相同名字,例如,org.dogclub.dogname or com.mycompany.buildingNumber。类型可以是类型中的一个,或者是一个类型的多值列表。

下述代码田间一个自定义属性,然后删除了它:

    NSNumber* stringProperty = [NSNumber numberWithInteger:kABStringProperty];
    NSString* testProperty = @"com.apple.devpubs.testProperty";
    NSDictionary* dict = [NSDictionary dictionaryWithObject:stringProperty
                                                     forKey:testProperty];
    NSInteger result = [ABPerson addPropertiesAndTypes:dict];
    NSLog(@"Added %d properties.", result);
    result = [ABPerson removeProperties:[NSArray arrayWithObject:testProperty]];
    NSLog(@"Removed %d properties.", result);

八、创建和使用地址簿行动插件

地址簿应用程序的一个特性在于它能够在处理包含在人员卡片上的数据。你可以安装自定义插件给特定记录添加额外的action。已有action的一个例子是Large Type action,该action可以处理任何电话号码条目。当从上下文菜单中选择后,它可以大类型跨屏显示号码。

每种action插件仅能实现一个action。Action只能通过标签应用到条目上。一个action可以在地址簿应用程序中显示简单窗口。如果你的action需要做些别的事,就需要启动你自己的应用程序来执行该action。

ABActionDelegate协议必须跟在地址簿应用程序之后来识别插件,该协议总结在表1中。更多细节参见ABActionDelegate Protocol Reference。基于C的action必须实现一个名为ABActionRegisterCallbacks的函数,如Address Book Actions Reference所述。

表1 地址簿action插件的action方法

方法目的
actionProperty返回NSString常数,用于识别action所应用的属性。
titleForPerson:identifier:为action返回菜单条目的标题。该方法不应该返回nil。
performActionForPerson:identifier:为插件执行相关的action。每种插件仅能有一个action。
shouldEnableActionForPerson:identifier:如果action可用则返回YES,否则为NO。这使你的插件能够启用和禁用它的菜单条目(可选)。
要创建一个插件,使用Xcode New Project窗口中的地址簿action的插件模板。该模板可创建一个action插件,用来在任何电话号码上设计创建一个上下文菜单条目。当菜单条目被选中后,示例插件将使用Mac OS X的语音合成框架报出该电话号码。用你需要的插件代码替换示例代码。当你构建了自己的醒目后,将完整的bundle包放在 .../Library/Address Book Plug-Ins下。

当action插件被加载后,它的菜单条目会在上下文菜单中显示出来,带有从titleForPerson:identifier:方法返回的标题;这个方法不应该返回nil。插件可以用shouldEnableActionForPerson:identifier: 方法来启用和禁用该菜单条目。


九、导入和导出人员和组记录

可以使用vCard格式导入和导出人员记录。要创建人员的vCard表示,使用ABPerson的方法vCardRepresentation。该方法创建一个NSData结构,可用于编程或保存到一个文件中。要想能够拖拽该数据,使用一个在“Dragging Files”中描述的文件promise。

要从一个vCard表示创建人员记录,使用ABPerson方法的initWithVCardRepresentation:。


十、使用地址簿C API

大多数情况下,Objective-C API与C API有相近的方法和语法。这使之容易确定哪个函数是和特定Objective-C方法相对应的。

主要不同之处在于开发人员下要了解何时使用地址簿C API:

人员拾取器只能在窗体表单中出现,而无法使用C API来设置一个可访问的视图。另外,选择和显示属性上的改变会通过Carbon Events发送。

当创建一个基于C的action插件时,你的bundle必须实现一个名为ABActionRegisterCallbacks的函数,它将返回一个ABActionCallbacks 结构。该结构需要根据该类型定义格式化:

typedef struct {
    // The version of this struct is 0
    CFIndex                      version;

    // A pointer to a function that returns the AddressBook
    // property this action applies to.
    ABActionPropertyCallback     property;

    // A pointer to a function that returns the AddressBook
    // property this action applies to. Only items with labels
    // may have actions at this time.
    ABActionTitleCallback        title;

    // A pointer to a function which returns YES if the action
    // should be enabled for the passed ABPersonRef and item
    // identifier. The item identifier will be NULL for single value
    // properties. This field may be NULL. Actions with NULL enabled
    // callbacks will always be enabled.
    ABActionEnabledCallback      enabled; 

    // A pointer to a function which will be called when the user
    // selects this action. It's passed an ABPersonRef and item
    // identifier. The item identifier will be NULL for single
    // value properties.
    ABActionSelectedCallback     selected;

} ABActionCallbacks

要使用C编程结构访问用户的共享地址簿,你需要使用 ABGetSharedAddressBook返回的的值。

ABAddressBookRef addressBook = ABGetSharedAddressBook();
将其与使用Objective-C编程接口比较,可发现在对应的方法和函数名之间的映射关系。

ABAddressBook *addressBook = [ABAddressBook sharedAddressBook];
这个例子来自“搜索地址簿”的列表1,在当前用户地址簿中搜索所有名为Smith的人员并返回结果数组。

列表1 一个简单的数组,使用Objective-C

ABAddressBook *AB = [ABAddressBook sharedAddressBook];
ABSearchElement *nameIsSmith =
    [ABPerson searchElementForProperty:kABLastNameProperty
                                 label:nil
                                   key:nil
                                 value:@"Smith"
                            comparison:kABEqualCaseInsensitive];
NSArray *peopleFound =
    [AB recordsMatchingSearchElement:nameIsSmith];

列表2使用C API实现了相同的搜索。注意对应方法和函数名之间的映射。

列表2 一个简单的搜索,使用C

ABAddressBookRef AB = ABGetSharedAddressBook();
ABSearchElementRef nameIsSmith =
    ABPersonCreateSearchElement(kABLastNameProperty,
                    NULL,
                    NULL,
                    CFSTR("Smith"),
                    kABEqualCaseInsensitive);
CFArrayRef peopleFound =
    ABCopyArrayOfMatchingRecords(AB, nameIsSmith);
关于在地址簿框架上使用C API的更多细节,参考 Address Book C Framework Reference for Mac OS X.

十一、修订历史

DateNotes
2010-08-03Minor editorial changes throughout.
2010-02-24Updated the sections Using the People Picker, Searching an Address Book, Using Address Book Groups as Distribution Lists, and Creating and Using Address Book Action Plug-in. Added sample code to Adding Properties to Address Book Records. Formatting and editorial changes throughout.
2009-08-07Minor changes throughout.
2009-05-28Made minor updates to searching and drag-and-drop exporting.
2006-04-04Made minor editorial corrections throughout.
2005-04-29Made minor sample code changes. Added important note about syncing with Sync Services to the introduction.
2004-04-21

Added major updates. New sections include “Creating and Using Address Book Action Plug-ins,” “Using the People Picker,” and “Using the Address Book C API” for Carbon developers. Other sections have new sample code and more detailed content.

2003-10-30

Made minor correction in “Search Examples.”

2003-08-21

Added revision history, which records changes to the content of Address Book.



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值