Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2003-417
$eventId
ID of event: wwdc2003
$eventContentId
ID of session without event part: 417
$eventShortId
Shortened ID of event: wwdc03
$year
Year of session: 2003
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC03 • Session 417

Using the Address Book Framework

Application Frameworks • 52:20

The Address Book framework provides a simple set of APIs for accessing a centralized repository of contacts. After a brief overview, this session focuses on enhanced-address properties and APIs, support for AppleScript, the People Picker user interface, and much more. Learn how to leverage this framework within your application to save substantial development effort and deliver a more consistent user experience across Mac OS X.

Speakers: John Geleynse, Henri Lamiraux, Scott Herz

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

All right, well, welcome to session 417, Address Book Framework. The Address Book Framework provides you with the functionality that's needed by your application to deliver a really great user experience and consistent user experience because it enables your users to get access to contacts in a consistent manner between your application and the way they would access contacts in another application.

And that's particularly true in Panther as we introduce the People Picker, which gives you consistent user interface for that. So this session-- I love this technology because it's just really well-- it's nicely self-contained and it's a straightforward API. I think that works great. And the feedback we've gotten from developers is that it really confirms that. So I would like to introduce l'incroyable Henri Lamiraux to the stage to talk you through this stuff.

Good afternoon. That's not me. That's me. So we're going to talk this afternoon about the Address Book Framework. So this is the agenda for the session. The first thing we're going to do is that I'm going to give you an overview of the Address Book Framework APIs. I'm not going to go into a lot of details like I did last year, because also I want to concentrate on what's new in Panther for the Address Book Framework. So I'm going to talk about the new properties and APIs we've introduced for Panther, and also talk about the enhancement to the AppleScript we've made, and talk about the People Picker, which I think other people seem to want to have, and also talk about the Action Plugins.

So let's start with the Address Book Framework overview. So before Jaguar, this was a situation. We had a lot of applications, and each one of them was managing their contact in a different manner, which was giving the end user a very bad experience, because they had to re-enter the same information over and over in several applications.

So for Jaguar, we decided to say, OK, we're going to create a centralized storage for contacts, so the user has only one place. He can enter the information once, and then any other application can reuse this information. So this was the situation on Jaguar. We introduced that on Jaguar, and it's been very successful.

We had a lot of third-party clients adopting the Address Book Framework. And we had a lot of shareware. As soon as we shipped Jaguar, we saw a lot of shareware coming out using the Address Book Framework API, which was a great thing. Internally, we have several applications using the framework. Address Book, for sure, which we introduced a brand new Address Book application for Jaguar.

We have mail. We use the Address Book to do mail completion when you type an email. iChat uses it to map the email to the real name of your bodies. Akal uses it to pick up people so you can have meetings. Sherlock in Panther also uses Address Book now. And Safari also uses Address Book.

And Safari has a great feature, which is the autofill web form, which you can turn on in a preference. And it allows you to very quickly fill up a form using Safari from your Mi Card. You can automatically fill up your form. It's a great, great feature. It works very, very, very well.

So what is the Address Book Framework? The Address Book Framework is a set of APIs, and it's accessible from Objective-C for Cocoa Developer, C and C++ for Carbon Developer, and also AppleScript. It's thread-safe. We made sure of that. It was very, very important for us that it was thread-safe.

And also, it has concurrency-- it supports concurrency to access the database, which means several applications can access the database at the same times and hopefully the database will be OK at the end. So we support concurrency very well. We have already several applications in the system using the Address Book Framework altogether, and this is a very important feature of the framework.

We want it also to be extensible, because we want you to use the framework. We want you to be able to add your own properties and not be limited to the properties Apple decided that they were the right properties. So you can extend the property set of the database. Thank you.

So I'm going to go a little bit deeper now and look at several of the classes and API that are part of the Address Book Framework. Let's start with ABRecord. So ABRecord is basically a row, one row in Address Book Database. The Address Book Database is a very basic type of database with rows and columns, columns being the properties, and each row is represented in memory by an ABRecord.

Every ABRecord has a unique ID, which so you can easily remember this ID, comment back the deleter, reopen the database, and ask for the record with this unique ID. This is the unique ID. It's completely unique. The A/B record has two important subclasses, which are A/B Person and A/B Group. So as I said, think of the A/B record as a dictionary of property-value pairs. You have a property and it has a value.

So some of the API on AB record. The first one is unique ID. You can get access to the unique ID for this record. There is no set unique ID. Unique ID is a read-only property. And a unique ID is just a string. And then you have a set of APIs to set a value, so set value for property, for a given property, set the value for this property, be able to retrieve a property using value for property. Property are just strings.

And the last one is remove value for property. We have this notion in the Address Book Framework where there is a difference between a zero and not set. So we have this notion of if a property is not set, you're going to get nil, which is different than returning zero or an empty string. This is a very important notion. So you can really remove a value, which means it's not anymore set, which is different than set it to zero or the empty string or whatever.

So let's take an example. Let's say that I have a record, which I obtain some other way we'll see later on. And what I can do here is that I can ask for the value for some property, and I will be returned a value. And here I can test. If the value is nil, it means it was never set before. So I can do something. Or if it was not set, I can do something. If it was set, it was different of nil, I can do something else.

And then I can set back this value for the given property. So very straightforward. Value for property, set value for property. So what are those properties? So a property has a unique name. They have to be unique, so you can't have two first name properties. It has to be a unique name across the database. So it's a name, like first name, last name, et cetera. And it has also a type, which can be integer, string, data, dictionary. So that's what a property is. This is a list of the data we support.

And here it says single value types, because a property of type KAB string property means there is only one string in this property. So we have string, integer, real, date, data, dictionary, array, everything that Core Foundation supports, all the standard types. So this is important. This is for single value property. It means the value contains only one value, one string, one integer, one real.

So example of single value property. First name is a string property. Last name is a string property. Birthday, you only have one. It's a date property. But we discovered very quickly that doesn't always work. For example, I'm sure most of you have more than one email and more than one phone number. So we could have said, OK, we're going to have home one property, or home email one property, home email two property, et cetera. But that didn't work very, very well.

What we wanted to be able to do is having one property and associate with this property multiple pairs of label and value. So for example, this person has three emails. He has two home emails and one work email. So we wanted to have one property associated with multiple instances of label and value pairs.

So we've introduced this notion of multivalue, single value, multivalue. And the class that encapsulates the notion of multivalue is called ABMultivalue, which is kind of straightforward. I'll capture the notion of a multivalue. Think of a multivalue as another type of collection classes like NSDictionary or NSArray. And the same way you have a NSDictionary and NSMutableDictionary, we have also a mutable and non-mutable flavor of the ABMultivalue.

So what are those multivalues exactly? A multivalue is just an array of triplets. For each value inside this multivalue, there are three things. There is a label, an identifier, and a value. So what's a label? A label is a label, like home, work. It doesn't have to be unique. It can be anything you want. It can be anything the user decides, my vacation, home, whatever. It doesn't have to be unique. And that's a label.

The identifier. So, wondering why do I have this identifier? Label and value should be enough. The problem is the following. The label is not unique. So, you can't say I want my own phone number. You may have two or three in there. So, using the label to be able to identify which value you're talking about is not enough.

The index also doesn't work. You can't say I want the third phone because at one point, another application can come and completely reorder this multivalue and your third email doesn't point to what you thought it was pointing to. So, you can't use the label and you can't use the index to be able to reference a value within the multivalue. So, we introduced this notion of identifier, which is a way for you to remember which phone number you were talking about. Let's say that you had an application that you had the selection of one email.

And you want to remember the selection between launch of your application. So, what you can do is remember the identifier for this email. And next time you launch your application, you can find which email was selected the previous time. You cannot use the label for that and you cannot use the index for that.

And finally, you have the value, which is really what the user really wants to have in the multivalue. And the value is the only thing that's important with the value is that all value within the multivalue has to be of the same type. So if you have a multi-string property, all the value within this multivalue has to be strings or date or whatever. So the same way we have a single value, we have also a multivalue. So for string, we have multistring value, multiinteger property, multireal, et cetera, and multidate, multidictionary. So we have, for each type of single value, we have also a corresponding multivalue.

So let's take a couple of examples to see exactly how this stuff works. Let's say that I want to display all email addresses of a person. So I have a person, which I got from some other way. And first thing I want to do is say, OK, give me the email of this person. So you call person value for property. And the property name for email is kabemailproperty. And we return you an ABMultiValue, which is all the email of this person.

So now you can say, OK, tell me how many emails are in there. So email counts, and you get a number of emails. And now I can do a little loop and print each one of them. I can use an index here because when I call value for property, I return my own copy of those values. So this is mine.

If someone else changes the database while I'm iterating, it's fine. This is an in-memory copy of all the emails of this person. So here you can use an index. So I can say emails labeled at index, print the index, print the label, and print the value. And I will list all the emails of this person. So let's see how you can change someone's data. So I want to add an email address to a given person.

So I have a person. I do the same thing. Person, value for property, KAB email property. And I'm written on the multivalue corresponding to all the emails. The thing here is that I'm getting here a non-mutable multivalue. So before I can change it, I have to make a copy. I have to make a mutable copy.

This is the same kind of thing you have to do with dictionaries of array when you're programming in Cocoa or in Core Foundation. So I have to do a mutable copy first. And now I'm getting an AB mutable multivalue. And now I can add a new value in there. So I can say, mutable emails, add a value, the email, and give a label.

The Address Book framework provides a bunch of predefined labels for common things, like home email, work email. So here I'm just going to use a built-in label, which is the home email label. When I modify my multivalue, I can now set it back using set value for property. And because I made a mutable copy, I have to release the mutable emails. And here I added an email. So let's talk now about AB Person.

So, AB Person, as I said, is a subclass of AB Record. So, all the API I showed you on AB Record are also available on AB Person. And an AB Person represents a person. And here are some of the properties that we, just a sample of the property we support for person. We have first name, last name, birthday, email, phone, addresses.

We have a lot more, but this is just an example. I just want to point out to the last one, address property, which is of type multi-dictionary property. So, first of all, a person can have multiple addresses. I mean, like my home address, my work address, my vacation address. So, it needs to be a multi-value.

But then, what is an address? Sometimes you want to, you think about an address as a whole thing. It says, what is your address? And you don't think about the part that composes the address. You're just thinking about the address as a whole. So, this is why we're using a dictionary for address. Like that, you can use a dictionary as just my address.

But then, you can also dig into the address and say, what is your zip code? What is your street? What is your city? And we have key defined for each one of those fields for the address. So, KAB address property is one of the most complex property we have, which is a multi-dictionary. So, you have this three level of data. You have the multi-value. You have a dictionary for each address. And then, you can dig to the field of each address. Thank you.

The next one is AB Group. AB Group, like AB Person, is a subclass of AB Record, and it represents a group of people or records. You can mix records and people within a group. The only thing we don't allow is to have circular references, so we flag for that, and we won't be able to add groups inside each other and make loops.

There is only one standard property for a group, which is the name of the group of type string. So let's now move on on the Address Book class itself. A/B Address Book. The A/B Address Book represents the Address Book database itself. And it's located in the user library application support Address Book.

There is a folder there that contains the database plus a bunch of indexes and things like that, and the images. But for the user, everything is in application support Address Book. Within your application, there is only one unique shared instance of the Address Book class. You cannot create multiple instances of the Address Book class.

So let's look at the API. The first one is, how do I get to this instance? So we have a class method called Shared Address Book. It allows you to get access to the Address Book instance. So what you can do on the Address Book is that you can save. You make a bunch of changes. I didn't show that before, but you make a bunch of changes, set value, set value, set value. And at one point, you need to be able to save your changes, to commit your changes to the database.

So we have this save method on Address Book that allows you to commit all the changes you've made so far. So for performance reason, it's much better to do a bunch of set value, set value, set value, set value, and then do your save. Don't do a change, save, change, save. That's not too efficient. Do a bunch of changes and just save all your changes at once.

We also have a method that says, are there any pending changes? There is one special person in your Address Book, which is yourself, or myself, I think. And that goes at the MiCard. So we have a method that returns the MiCard, the card for the user of this account. So you can get the MiCard, and you can also set the MiCard if you want to change it to another person.

As I said earlier, every record has a unique ID. So you may imagine your application wanted to save away some reference to some specific person. So to do that, you will save the unique ID of this person. And you have a way to go back to the record corresponding to this person by calling recordForUniqueId.

You have a unique ID that you save in your preference somewhere. And you can get back the record by calling recordForUniqueId. You can add a record and remove record from the database. And so you can get access to all the people or all the groups that are in the database.

So let's get some example here. Let's say that I want to add a person named John Doe to my database. The first thing I'm going to do, define a couple of variables, an address book and a new person I'm going to create. I get access to the address book. And I create a new person by allocating a new person. And now I can set the value. So I can say set value for first name John.

And I also can say set value for the last name, Doe. So now I have this person, but it's just there. It's not added yet to the database. So I have to add it. I say Address Book, add this record. As a good Cocoa programmer, I have to release the person I just created, because it's been added to the database, so I don't care. And I have to save. I have to save my changes that will save this new record and insert it into the database. Searching. Searching is an important part of the Address Book API.

And the API you have to use to do searching is called Record Matching Search Element. What it does is that you build a query, and it will return an array of the people that match your request. So it will return an NSArray of people or groups, depending what you're looking for. And how you build this query is by using what we call an AB Search Element.

which I'm going to talk right now about. So the A/B search element is the brilliant block to make for queries. Let's take an example. Let's say you want to find all the people that live in New York and have a Mac.com account, a Mac.com email address. So you have to build three search elements.

The first one which says find all the people that have their home city being New York. And the second one would be find all the people that have an email containing Mac.com. And then you combine those two sets. Find all the people that have one and two. So by building those small search elements, combining them together using and and or, you can build more complex queries.

So how do you create one of those search elements? It depends if you want to search on person or group. So we have class method on AB person and AB group to create those-- Those search elements and the method is search element for properties, for property label, key value comparison. So the easiest thing is to get, take an example and see how does that works. So let's say that I want to search for all people that have John as first name.

So define a bunch of variables, Address Book. I want to create a search element, so I define a search element, and I define my resulting array where I'm gonna get the result of the query. I want to search for all the people, so I'm calling the method on AB_PERSON.

on AB Person. So I'm calling on AB Person, and I'm calling search element. I'm looking for the first name. So search element for property, first name. I'm skipping the label and key. We'll talk about that later. And the value is John. So every person searching for property, first name, value John.

And I'm going to do a case-insensitive search. You never know how I spell the name. So case-insensitive search. And I'm going to call-- I'm going to get access to my address book instance and can call now record matching search element. And I will be returned an array containing either empty or containing a list of people whose first name is John.

So let's do something more complex. I want to search all people living in New York. Same thing here. I'm looking for people. So I'm going to call the class method on abPerson. I'm looking for people living in New York, so this is the address property. And I say, living in New York, so most likely, I want people with their home label-- their label is home, so I'm searching for label AB home label. And I say for the city of New York. So here, I'm using one of the key I was telling you before about the address, the street address, which is a dictionary. And the key for city is KAB address city key. So I pass that.

The value is New York. And I'm doing also here a case-sensitive search. And I can call record matching search element with my element. So you can build those little search element and combine them also to create more complex queries. So there is an API to do that, which is search element for conjunction.

So conjunction being either or an end. And you pass it a list of children. So if you want to end two search element, you pass an array of two search elements you've created before. And you can end them together. and get another search element, which is the union of those two.

So I've been showing you the Objective-C API, but for people who are using Carbon, we have this full set also of API for C and also for AppleScript. So the C API are basically identical to the Objective-C. They are functionally identical to Objective-C. Everything that I show you in Objective-C can be done also in C.

We use the same CF naming convention and style that Core Foundation uses, and objects are represented by opaque types like AB Person Ref, AB Group Ref, AB Multi-Value Ref, same way that we have CFStringRef or CFArrayRef. So it's the same. If you're used to the CF naming convention, it's exactly the same.

So let's take an example. This is, for example, adding a person whose name is John Doe. So instead of being Objective-C, this is C. And the method is a, b, get shared address book. And you get an address book ref. I can create a person by calling a, b, person, create. And I get back a person.

And I can call-- because a person is a record, the method to set a value is ab_record_set_value, giving the person, giving the string, and the property is kab_firstname_property, something for the last name. And now I can add my record to the database. I'm missing two things here. I should release-- I call ab_person_create, so I should have a cf_release_person. Otherwise, you're going to have a leak, and that should also save. The CF release is important because if you call any create method, you should have a release method somewhere in your code. Otherwise, you will be leaking a bunch of references.

AppleScript support. Same thing with the C. We have full access to all the API of the Address Book Framework in AppleScript. The only difference is that here you're not talking to the framework. You can't say, "Tell Address Book Framework." You have to say, "Tell Address Book Application." So you have access to all the AppleScript, all the Address Book API through the Address Book Application. and I will show you later some enhancements we made to AppleScript.

So I have an example here. Tell application Address Book. What I'm doing here is that all people will contain an array of all the people in my database. Set all people to people. So now I'm going to iterate through each person in this array. And here, what I'm doing is that I'm getting the multivalue containing all the email of this person. Set all emails to emails of a person. So all emails will be a multivalue containing all the email of this person. And I can go through each value within this multivalue and extract the email and print it.

So you have the same functionality you have in AppleScript that you have with C or Objective-C. So this was kind of a quick overview of the Address Book API. I think that covers basic functionality. There is a lot more, but I'm not showing it here today. I want to concentrate now on what's new in Panther for Address Book.

So for Panther, we listened to you. We had a lot of good feedback from developers about the Address Book Framework. And so for Panther, we decided to plug some of the hole in the APIs or property that we had in Jaguar. So we've added a couple of APIs or properties. And also, we've enhanced the performance. The Panther version is much faster at saving, at doing search. So better performance, and for sure, we fixed a couple of bugs.

So let's talk about the new properties and API that we've introduced in Panther. So in Jaguar, we had one property called the birthday property, which was a single value for the birthday. We've added a new property in Panther that has we called it author dates. We didn't find a better name for that, which basically author dates.

And there is one predefined label which is the anniversary. We kept the birthday because after all people have only one birthday. So we say, yeah, that should be a single value. So we kept the birthday property as is, but we've added this new property which can contain whatever dates you want.

We've also added a property called RelatedNamesProperty, which is just a multi-string. And we are defining a bunch of labels on that-- father, mother, friend, associate. You should check out the Panther version of Address Book. We have a nice usage of this property where we can link cards. So you can jump from one card to the other using this property.

We value the department property to be more compatible with other contact management applications. And we finally made available the Person versus Company flag. I think it was one of the most requested features in Jaguar. In Address Book, you have this notion of a person can be a person or a company. And that was not available in Jaguar. We made that public in Panther, so now you can decide if it's a person or a company.

And also, we made available the name ordering, which in Address Book application, the user can-- for each card, they can decide if the name should be first name first or last name first. And that was not also available in the API in Jaguar. So there is a property called KAB Person Flags, which is just an int. And we provide also all the mask for you to be able to test if this card is a person or a company.

Also, with AIANTS, the notification that we are sending, in the Jaguar, we're sending two notifications. One that says, the database has changed. It means your process has changed the database. You've just changed the database. And there is another one called database change externally, which means some other process has changed the database. In the Jaguar, we were just sending them and say, OK, go figure out what changed. We were not telling you what was changing.

In Panther, now we were telling you what changed. And the info object that sends the notification is now a dictionary, and it can contain three keys, KAB inserted records, KAB updated records, or KAB deleted records. And each one of these keys has an array associated with it, which contains a unique ID of the record that has changed. So, like that, you can optimize your refresh. Before, you basically had to refresh everything because you didn't know what had changed.

Now, you can tell if what you're displaying on the screen has changed or not. We may send you, in a case where everything changed, for example, the user reversed from backup. We have this backup mechanism in the address book. If the user reversed from backup, basically everything changed. So, in this case, the info object will be nil. So, if it's nil, it means everything changed. So, do like we were doing in Jaguar.

We also give you now a way to figure out what a unique ID is pointing to. So you have this method called recallClass from unique ID. Given the unique ID as inside the class of the object. So this method allows you to retrieve the class. So we can either say it's an AB person or an AB group.

This is very useful for the notification I was talking before, because in the case of deleted records, we send you an ID, but this ID doesn't point to anything anymore. So if you want to know if it was a record, a person, or a group that was deleted, you can use this method to figure out what was deleted.

We also now expose some of the preferences that are in the Address Book application. If you go to the Address Book application, the user can decide what the country to use to format street addresses. We have a method now that we can tell you what was the user setting, and we return you an ISO country code, US, FR, UK, so you can know which country the user is in.

By the way, we use this country code to format the address differently depending on the country code. The order of the fields are different when you format it. You have the same thing for name ordering. The user can decide globally for his address book if he wants first name first or last name first. So now you can figure out that using the default name ordering API.

Finally, we have this formatted address from dictionary. So remember, an address is represented as a dictionary in Address Book. So if you pass one of those dictionaries to this method, we're going to return you a string that's formatted correctly depending on the country code of this address. And this string can be printed on a label as is. So we do all the formatting for you. We go even farther.

Notice that we don't return a string. We return an attributed string. So we use attribute to embed inside the string where are each field. So we put the key-- the key of the attribute is a key we use in a dictionary. So for example, zip code, street one, city.

And we also embed in there the localized description of the field, code postal for France instead of zip code. So we put all this information into this string. If you don't care, you can just print it as a string. But if you want to format it differently, put the zip code in bold, you can parse this attributed string and find exactly where is the zip code in this string.

Enhanced AppleScript support. So we've also enhanced AppleScript, as I was mentioning. So now we have much better parity with Objective-C and CPI that we had in Jaguar. We have support for all the new properties we've introduced in Panther. And now you can do things in AppleScript you could not do in Jaguar. For example, get and set the MiCard, get and set the company flag, a person is a company or a person.

Get and set the image for this person, and also have access to the Address Book selection, so you can see exactly what's selected in the Address Book application. And we can also export vCard using AppleScript. We've added an AppleScript menu to the Address Book application. So if you write script, now the user can put their script in their library Address Book script, and they will show up in this menu.

And let's move on to the People Picker. So the People Picker is something we made available internally at Apple in Jaguar. And it was used by Mail and iChat and iCal. The People Picker is basically a read-only version of the Address Book. It's a smaller version of the Address Book.

What's very interesting with the People Picker is that it provides a consistent UI for people to pick people or groups, like the same way we have a phone chooser or a color picker. And so we decided to make that public in Panther so your application doesn't have to reinvent the wheel and can just use this UI very quickly and integrate it into your application.

So the People Picker, this is the email People Picker. The UI, the look hasn't changed from Jaguar. It's the same look. And there are several areas in the People Picker. There is a group column, similar to the group column in Address Book. We have the name column, which lists all the people that are in a group that's selected in a group column. We have the value column, which in this case only has one property, which is the email. But you can have multiple one if you decide to.

We have the search field that works exactly like in Address Book. So people can use it to search and limit the number of displayed people. And for Koku only, we have an auxiliary view where you can add your own control to the People Picker. Mail adds a To and CC button, but you can add your own controls there if you want.

People Picker is available for Carbon and Cocoa. For Carbon, the People Picker is a window. And the header is in abpeoplepickerc.h. And it's a view for Cocoa, so you can put it in any window you want. And also, for Cocoa Developer, we've also built an interface builder palette to be able to use the People Picker. The palette is not automatically loaded in IB. You have to load it manually using the preference. But it's in Developer Extra Palettes. And I'm going to show you that right now.

Can I have the demo one? So I'm launching Interface Builder. And I'm going to create an empty nib and drag a panel. I'm going to make my panel, I'm going to call it People Picker, and I want it to be pre-sizeable. So now I have this palette here, which is the People Picker. I can drag it in.

I'm going to set the spring and strut so it resets correctly. And I can go to the Attribute Inspector. So here I can set up my People Picker the way I want. So I want to allow multiple selection. And I want to have multiple value selections, so I can select multiple emails or whatever. And I can add-- I want to display email, phone. And I'm happy with that. I can test my interface and I have a beautiful People Picker.

And it works the way you expect. You can search. You can search and everything, and it really works. Basically, the Address Book in one little view. For Carbon, you're going to get the same thing, except it's going to be a window instead of being a view you can put anywhere.

So let's go back to the slides. So the People Picker APIs are grouped in four groups. APIs dealing with the behavior of the People Picker, APIs dealing with the value column content, APIs dealing with the selection, and API dealing with the action, what happens when the user double-clicks or things like that. So the behavior, you can allow or not group selection.

You may have an application that only deals with groups. You don't want to deal with people. You don't want to be able, the user, to select groups. So you can turn on that, saying, I want to allow group selection. In this case, you'll be able to only select groups.

You may want also to allow or not multiple selection of group or people. Depending on your application, you may only allow one person to be selected and not two. So you can turn on and off multiple selection of group or selection, or group of people or people. And for the value column, you may not want selection there.

You may decide that you're not really interested by this column because, after all, you only want to select a person, but you still want to be able to display the email. So you don't want to select a single value within the value. So you can say no. You can say, I only want to select one value or multiple value at the same time. So you have a really full range of possibilities regarding the selection with those APIs.

You can also set up, if you don't use IB or if you're using Carbon, you can also set up the content of the value column. You can also not having a value column if you decide to. If you just want to have groups and people and no value, that's fine too.

But also you can set what value you want to be, what property you want to display in your value column. You can choose as many as you want and you're going to have a pop-up at the top and be able to switch back and forth. And so you can set which value you want to be shown by default when the first time you launch your People Picker.

You can also have access, you can set and get the selection for the group, for the person, for the people column, for the values. So you can get what's selected in the current state. And also, you can react to the user action. So for Cocoa, we use a regular target action paradigm, so you can make connection between the People Picker and the rest of your controllers. And also, you're going to get notification when something is redrawn or something gets selected, like you get for the table view or things like that. The same kind of paradigm you're used to.

For Carbon, we're using Carbon Events. So we have a suite of Carbon Events that you can install handlers, and you're gonna call you every time something happens. So now, I would like to bring on the stage Scott Herz, who is going to be talking about the new Action Plugins.

Can we go back to the slides real quick? That would be me. That would also be me? Okay. So I want to talk a little bit about our action plug-ins. Some of you may have seen kind of what I'm talking about. If you go into the actual Address Book application, you can see those little rollover menu jobbies. And rather than... Oh, that's what that's for. I'm supposed to be showing you this stuff. That's what I'm supposed to be doing.

Can we go back to the... All right, this is much more entertaining. So we've got our card here. And these are the rollers I was sort of telling you about. The user can move their mouse over certain data elements, like homepages, emails, phones, and things. And when they click, a menu will pop up.

And before, that was just our menu. And now the point is that we want this to be your menu as well. So I've made a little plug-in here that's going to do a mnemonic for a phone number. And we can just sort of cycle through them, pick the one that we want. I'll grab that one. And there's all sorts of other ones.

So that's what we're going to try to build here in a second. So can we switch back to the slides for real? The reason we want to do this is we've already doubled the number of plugins that we've added since Jaguar. And the fact that we came up with six or so new ones and there's only two of us, three of us, we figured you guys would be able to come up with all sorts of cool stuff, especially in terms of things like SOAP and web services type things. A lot of those are really people-driven, and so we think this is a really good way to get at some of those things. So plugins can be written in all sorts of languages.

Objective-C, C/C++, or AppleScript. The Objective-C ones are going to be NSBundles, and the C/C++ ones are going to be CFBundles. And then the AppleScripts, they're just scripts. You go ahead and throw those guys in -- I'm sorry, the headers you'll need to figure out how to do the Objective-C and the C++ ones can be found in addressbook abactions.h and addressbook abactions.c.h.

So once you've made one of these bundles or a script, you can put it in the usual search paths if you want it available for everybody on the system. You can put it in /library/addressbookplugins. If you only want-- it's a little unstable or whatever and you're just testing it, you can put it in your home directory until the library address book plugins.

So how does this work? What's the process like? The first thing that's gonna happen is Address Book has to be running, so Address Book is gonna launch. And what we're gonna do is we're gonna go through all of those two source paths, and we're gonna try and come across, try and find all the bundles that you've put in there.

And we're gonna kind of iterate through them and we're going to ask each one of them which property do you work with. You're going to return I work with phone so return something like KAB phone property. We're going to remember that. At some point the user is going to click on one of those rollovers. When they do we need to start building that menu. We're going to go through the plug-ins again that correspond to in this case let's say it's e-mails.

We're going to say okay, plug-in, what title do you want to be? You're going to say I want to be Bob's cool e-mail plug-in or whatever. You'll return that. Then we'll ask you okay, do you want to be enabled? We'll pass you some information you'll see in a little bit.

You can decide maybe for -- a good example of where we do this in address book ourselves is if you roll over an e-mail and it's an AOL.com e-mail, one of the plug-ins we have is show AOL home page, so the actual text is show AOL home page versus if it's a .mac it will say show .mac home page.

You can tweak yourself there. Or if it's something you don't -- sorry, if it's something you don't know you can say I'm not -- I don't know what I would do for this case and you can return no. If they do finally click on you, you can say, we'll call your perform action, and then you can go do stuff. So here's the Objective-C API for it. It's pretty much just like I described.

We'll call you, we'll call your action property. You'll return the property that you're interested. These are pretty much any of the properties that you're going to find in abglobals.h. The limitation there being, you've probably noticed in Address Book, we don't do roll-ins for things like first name, last name, some of those things up across the top. So we don't do roll-overs for things like notes. Not to say that at some point in the future we won't, but we just don't right now. So don't expect it.

So here's the call that we're going to call when we want your title. And we're going to pass you the person of the currently selected card. And then we're going to pass in an identifier. So what you can do is you can pull out from that person using the identifier the exact item, the exact data item that the user has rolled over.

And once again, if you want to be enabled, this is optional. If you don't implement this, we just assume that you want to be enabled all the time. And so maybe your plugin only works with Canadian addresses or something, and you can look in the identifier and figure out, oh, this is a US address, and return no.

And then, if the user, if you're lucky enough and the user's actually chosen your item, they'll go ahead and we'll call perform action for person, passing all the same information again, and you can go, like in that case, we put up our panel. do that kind of thing.

So that was Objective-C. C plugins work pretty much the same way, the exact same flow. The difference is that every bundle that you make, you're gonna have a function called AB_ActionRegisterCallbacks, and it's gonna return a pointer to a struct. And this struct you're gonna fill out with your own function pointers of these callbacks. So your property, your title, your enabled if you want, if not, just put null in there. And then the function that you want us to call when you're actually selected.

So I'm not going to walk through all of these, but this is the API there. Same thing, you're just returning CFStringRefs instead of NSStrings. Very much the same kind of idea. So AppleScript's a little bit different, but same process. We're just going to call your different-- your handlers for trying to get your property.

We're going to get you your title, and we're going to give you-- we're going to fill up a variable, you know, a person and a value with the same kind of items that you saw in the previous two examples. So that's all I have, and I'm going to bring up John Geleynsse here. The evangelist, so great they gave him two titles, it looks like. Hey, I want to thank you for not rejecting Canadian addresses. Never Canadian, always Canadian.

Right, so the conclusion sort of is that Address Book is, the framework has been adopted widely in Jaguar, and we think it's going to continue to be adopted widely and hope that everybody in this room uses it in their application. I think it's going to deliver great. It'll make Panther that much more consistent to use. And use the People Picker. Don't roll your own UI for that stuff.

and, you know, take advantage of the developer opportunity for the Action Plugins. More information, basically go to the user experience portal on developer.apple.com and click on the Address Book link. There's just, there's tons of stuff there and there'll be even more specific to what we've talked about at the conference here.

And, uh... There's other sessions that we think would be of interest to some of you. This stuff is pretty self-contained, so most of the information you need is pretty much given in this session. But if you're new to Cocoa, then check out some of the -- if you're watching the DVD, check out Introduction to Cocoa. If you're new to Carbon, check out the How to Write a Modern Carbon app or whatever.

There's other stuff that you want to go to if you don't understand any of the code that's up here. And then send me feedback. If there's anything about this API that's not meeting your needs or if there's something you'd like to see added or anything from a developer's point of view, please contact me and I'll get it through to the team.