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: wwdc2002-012
$eventId
ID of event: wwdc2002
$eventContentId
ID of session without event part: 012
$eventShortId
Shortened ID of event: wwdc02
$year
Year of session: 2002
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC02 • Session 012

Address Book Framework

General • 43:43

This session provides an overview of the Mac OS X Address Book APIs and details how to take advantage of them to handle contacts for your application. Learn how to leverage this framework within your application to save substantial development time and effort, and deliver a more consistent user experience throughout Mac OS X.

Speaker: Henri Lamiraux

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 the second-to-last session of the day, of the week, I think. Hope it's been a good week for you. This is probably the most important session in the week. Well, at least we'd like to think so. But this session is important because the Address Book Framework figures prominently into one of the steps that it takes for you to make your application a really great application on Mac OS X from here on out.

And that is because the Address Book Framework allows your application to leverage a centralized contact database, which means that every application, theoretically, in the future could be leveraging the same contact information, and the user will have a much smoother user experience in terms of saving contacts, changing them, editing them, and using them within multiple applications.

So if you think about that whole process, I think it really is an interesting proposal, and we would love to see next year. We're at WWC that there are literally, you know, tens or twenties or thirties or forties of applications that are leveraging Address Book. So to talk to you about how cool this is and how to use it, and I'd like to introduce Monsieur Henri Lamiraux.

Good afternoon. In this session, we're going to talk about the Address Book Framework, not the Address Book application, but only the Address Book Framework, which the Address Book application uses. It's been a long-standing request from developers. I think since Mac OS X 10.0, there's been a request for developers to have a way to access the Address Book.

This new API is accessible from Carbon, from Cocoa, and from AppleScript. We made sure that all our major frameworks can use it. It's thread-safe. You can access it from multiple applications at the same time. You can have mail or other applications, Address Book, and several other applications, both using the same Address Book Framework at the same time.

It's also extensible, which means that we provide... We provide a set of properties, but you can, as an application developer, add your own properties to the Address Book. Don't get too crazy. It's not a general database. It's not meant to be a general database. It's just to manage people and groups. But you can add your own properties if you don't provide it.

So, as John said, this is the Mac OS X centralized storage for contacts, for people and groups. And it's going to be used heavily in Jaguar. Mail uses it, iChat uses it, Address Book uses it, Sherlock, Setup Assistant, a lot of applications on Mac OS X will use this Address Book. So it's very important for the user that all information are in one place.

Adress Book Framework will be available on Jaguar and later only. And the database we use is not backward compatible. It means the database that's going to be produced on Jaguar won't be usable on 10.1. But we automatically import a 10.1 database into Jaguar and convert it the first time the address book is accessed, the address book framework is accessed. So any application, not only address book, but if Mel used the address book framework the first time, the database will be converted at this time.

So I'm going to go through the API. This is about API. It's not about fancy demos, but about APIs. And we have a lot of things to cover. So this is the overview, what we're going to be talking about today. So let's start. Let's start with AB Record.

The A/B record is really the base of the Address Book Database. It's basically a row inside the Address Book Database. Every A/B record has a unique ID, a real unique ID, using the CF unique ID. And it's an abstract base class for two other types of rows, which is the A/B person and the A/B group. Think of the A/B record as a dictionary of property and value. It's basically that. It's just a list, a dictionary of property and values, and with a unique ID.

So what are the APIs for AB Record? First of all, you can get the unique ID, which is a string, and as I said, a real unique ID. And then we have three APIs to be able to set a value. But also, I'm going to use in your slides Objective-C syntax, but the C APIs are basically the same.

So you can set a value, passing a value for a given property. You can get a value for a property, and you can also remove a value for a property. Which means that if you remove a value for a given property, next time you're going to ask for the value, it will be nil. We support the concept of nil in the Address Book.

Let's give you a sample of a piece of code using those APIs. You have a record. You ask for a value for a property, return the value. Now, depending if the value is nil or not nil, you're going to do something. You're going to do something here. Then you'll be able to set the value again for this given property. Very simple model.

[Transcript missing]

is a unique name. He has to have a unique name, like first name, last name. So if you start creating your own properties you're going to add to the schema of this database, you need to use something that's going to be unique, com.mycompany.whatever. So be sure that your property will be unique.

Each property has a type, integer, string, date. And also it comes in two flavors, single value and multi-value. So, single value type. We have KAB string property, which is matched to a NSString. KAB integer property, real, date, array, dictionary, data. Basically, the regular, the standard, p-listable type that's supported in foundation and core foundation.

Some example of single value property. First name is a string. Last name is a string. Birthday is a date. Nothing very, very bizarre or unusual. But if you think about it, single value works well for your first name, last name. You only have one first name, one last name, one birthday. But when you start talking about phones, emails, you don't have one phone, you don't have one email, you don't have -- we have more than one address, street address.

So single value properties don't work for this type of data. You still want to be able to say, "Give me your emails," but maybe multiple of them. And also you may have multiple home email, multiple home phone, multiple work email. So it's a bit more complex than just a single value. So what we need to be able to do for email and phone is associate a label, which is not unique, in a value.

So the type for multivalue properties are the following. They exactly match 101 with a single value property. We have multiple string, multiple integer property, multi-reol, et cetera, multi-date, multi-array, multi-dictionary, multi-data. And to be able to encapsulate this new type, we have created this new class, which we call AB Multivalue.

AB stands for A/B Multivalue Class. Let's say that you have a person. If you just want to get his first name, you're going to say Person ValueForPropertyFirstNameProperty. You're going to get a string. But for email, you're going to say Person ValueForPropertyKABEmailProperty, and you're going to get one of those new A/B Multivalue.

An A/B multivalue is basically another type of collection classes. It's like NSDictionary, NSArray, but it's a bit more complex. In one of the original designs we had, we were using a dictionary for a multivalue. The point is that we wanted to have multiple labels. So a dictionary, you can't have multiple home labels, you can't have multiple work labels. So we moved to this A/B multivalue. An A/B multivalue manages an array of triplets. You have a label, you have a value, and you have an identifier.

So the label don't have to be unique. For example, you have multiple home email addresses. The label can be any type of string that describes this value. We provide a set of standard labels, homework that you can use, but you can put whatever you want. The value within one, if you have an ABMultiValue, all the value inside this ABMultiValue object have to have the same type. So if you have a multi-string property, all the value has to be strings. If you have an ABMultiDate property, all the value must be of type NSDate.

The identifier provides you a way to reference one particular value inside this ABMultiValue. Don't forget you have multiple applications that can change this value. If you start to save away the index of one of them, I want to save, "Oh, my home is my third entry in this multi-value," some other application can come and completely change those values. Third, three, no point to maybe nothing or point to something that was not what you meant. We have this identifier that uniquely identifies each entry inside the multi-value. Multi-value is just basically an array of triplets, identifier, label, and the value.

A multivalue also has a notion of a primary. Maybe you want to be able to have a user interface when you say, call Joe. But Joe has five phone numbers. So you can say this is a primary phone number, the primary value for Joe. Or Joe has, in this example, Joe's primary email is [email protected], even if he has six or seven emails. So we have this notion of a primary value.

Like any other collection classes in the Cocoa framework, we have two flavors of this class. We have a ABMultiValue, which is a non-mutable object, and we have a MutableMultiValue. So let's talk about multivalue first. So what can you do with a multivalue? You ask for the KAB emails property. You're going to get an object which is an AB multivalue.

What you can do now is be able to say how many entries in there. So if it's an email, maybe it's going to be written five. It means you have five emails. You can also get the index. If you have stored somewhere the identifier for one of these emails, you can get the index at the present time for this identifier.

So you can get the index for an identifier. You can also ask for the value at an index, the label at an index, or the identifier at an index. You can also ask for the primary identifier. What is the identifier for the primary email, primary phone? And you can ask for the type. For example, if you have for email, it's going to return KAB multi-string property because every property for an email is of type string.

So let's look at some sample code using multivalue. Let's say that I want to display all the email addresses of a person. So I have a person. I'll show you later on how to get to a person. I have a person, and I can ask for give me the value for property, email property.

I'm going to get back an AB multivalue, which is all the email of this person. I can count how many emails are there. I can just do a loop and log in the value, the label, and the value for this person. So I will get home one email, work, another email.

So now the mutable multivalue. This is a multivalue you can change. The multivalue, when you get it from value for property, it's a non-mutable value. So if you want to make changes to this value, to this multivalue, you have to make it mutable. So for a multiple multivalue, you can add a value with a label, and it returns the identifier for this new value you just added, if you care about it.

You can insert a value with a label at a specific index. You can remove a value and a label at an index. And you can replace either the value or the label with another value and another label. And you can set the primary identifier, saying this is the primary phone number for this person.

So let's see some code here. Let's say that I want to add an email address to a person. I have a person. I asked for the list of email. So I get a multi-value email. But this is a non-mutable. Every value returned by value for property are non-mutable. Either string, everything is non-mutable. So I get the emails. I make it mutable by making a mutable copy.

So now I can add a new email, [email protected]. And this is his home email. So I use the KAB home email label, which is a standard label we provide. And now I can simply set this value again for the email property. And because I made a copy, I have to release my multi-value here. So this adds a new email to a person.

So we talked about A/B record and the properties. Now let's get into details to the real object you can manipulate, like person and group. So let's talk about person. So person, A/B person, is a subclass of A/B record, and it represents a person. So we have some standard properties for Person. I just put a couple here.

If you look at the header or the documentation, you'll see we have a bunch of standard properties for Person. So we have first name property, which is of type string. We have a last name, which is of type string. We have a birthday, which is a date. We have email, which is a multi-string. We have a phone property, which is a multi-string.

And we have also an address property, which is a multi-dictionary property. So I want to talk a little bit about that. When you think about address, street address, you can think about two ways. You can say, "Oh, give me the address of someone." So an address is just a block of data. It's just an address.

But sometimes you want to think of an address as something that's a street, a city, a zip code, a country, a country code. So to represent an address, we use a dictionary, which the key is the street, the city, the zip code, whatever. And because you can have multiple addresses, this is a multi-dictionary property. This is the most complex type we have. So like that, you can deal with addresses as, "Give me all your addresses. Give me one address. Or give me the street of address home." You can deal with the whole hierarchy of possible data.

ABRecord, as I said, is a subclass of ABRecord, so we have all the APIs of ABRecord, set value for property, etc. But also we have specific APIs for ABRecord. You can set an image on a person. We don't use NSImage because the Address Book Framework is a foundation-level framework, so we don't have access to NSImage or things like that. The only thing we have access to is NSData.

So to set an image on a person, you pass an NSData that contains the TIFF representation for the image. So you can say, "Person set TIFF image from data." And in the Address Book, you're going to see the image showing up. You can also get the image for a person. And a person that belongs to a group. So you can ask, give me a list of all the groups this person belongs to. Parent groups will return an array of all the groups this person belongs to.

Maybe a person will support vCard, so you can create a person from a vCard. We support the full 3.0 specs. So you can say, create init a person, init with a vCard representation, passing an NSData that contains a vCard representation. And you can also ask for the vCard representation for a person.

So let's talk about AB Group now. AB Group, like AB Person, is a subclass of AB Record, and it represents a group of people or a group of other groups. The only thing we don't allow is circular reference, so you can't have Group A being in Group B and Group B being in Group A. We don't allow that. We return an error. So aside from that, you can have a group with Person and other groups. You can do every combination you want.

AB Group has only one standard property we support, which is the name of the group. That's the only one we provide. You can add the other one if you want, the description or anything else. This is the only standard property we provide. So, like AB Person, AB Group inherits all the API from AB Record, and he has a set of API that are specific to AB Group. You can ask for the members of this group. It will return an array of AB Person.

You can add a member, which is a person, or you can remove a member from this group. same way with the other group, you can ask, give me all the subgroup of this group. You can add a subgroup and you can remove a subgroup. And also you can say, give me all the parent group of this group.

Groups also have something we call distribution lists. This is only valid for multi-value properties. First name, last name are single value properties, so distribution lists don't work. But everything that is multi-value, phone, email, street, you can create what we call a distribution list. It's most useful for emails, which in email is called a mailing list.

You want to be able to send an email to everybody in the group. Distribution lists allow you to do something like you have a group called friends, and you have a bunch of person in there. Each one of them has three or four emails, but for each person you want to pick one specific email. For John, you want his home email, but for Paul, you want his work email. So distribution lists allow you to do that, to set up a distribution list from a group.

So the API gets a little more complex here. So we have two APIs to deal with distribution lists. The first one, setDistributionIdentifier for property person. It means for a given person belonging to a group, for a property email, I want to use this identifier, which may be point to home, the home email. So you can set the distribution list like that.

You can also get information for a given person, what is the identifier for a given property. So for person Joe, for property email, what is the identifier for this email address. So you can build distribution lists like that, or mailing lists for emails. You can do it for, if you want to print labels or whatever, you can also use that for street, for phone numbers, whatever property which is a multi-value property. This is the set distribution identifier, identifier, the property, the person, and here to get, you want to pass the property you want to talk about and which person you are talking about. This is API on AB group.

So we're going from the bottom, AB record, AB person, AB group, and now we are at the top, which is the AB address book class. So the Address Book object represents basically the database. It represents the database of your contacts. The location of the database is in tilde, the home directory, library, application support, address book. You can only create one unique instance of Address Book. So if in your application you ask three times for the Address Book object, you're going to get always the same instance. And you can't create a new instance of Address Book. We always return the shared instance.

So the API on Address Book, you can get the share instance. Give me the share instance of the Address Book. You made some changes. You want to be able to save, or you want to be able to ask, do I have anything to save? Notice one point here that a lot of our API return a Boolean, depending if the operation succeeds or not. Most of our API return the Boolean just to tell you the operation is successful. We also, in Objective-C, we raise exception. If you pass a bad parameter, we will raise an exception.

So, save, add and save changes. We have the concept of a MIE. MIE is the owner, the person who is right now logged into the machine. So this is a very important concept. A lot of applications may want to ask, the only thing they care about the address book is the MIE because you want to get the shipping address or you want to get some other information. The MIE is the owner of the database.

The user in address book can set it up. You may also not set it up. So if you're using the MIE API, be ready to get nil because maybe the user never set it up. So the API is to get the me person, and so you can set the me person.

You can also get, if you have a unique ID for a record, you can get this record by calling record for unique ID, passing the unique ID. Every record has a unique ID, so you can save the unique ID somewhere else. You can get back the record that has this unique ID.

You can add a record. You can remove a record from the database. You can also ask for all the people and all the groups that are in this database. And we return an array of AB person and an array of AB group. So let's look at some sample code. I want to add a person to the database that's, his name is Joe Doe.

So I have two variables, an address book and the new person. So first, I'm going to ask, give me the share instance of the address book class. Then I can create a new person, any person I look in it auto-release. And then I can set my properties. So I want to set his first name and his last name. So, new person, set value, John, for property, first name.

New person, set value, Doe, for property, last name. And now I can add this person to the database, and I can save my changes. So you can do a bunch of changes and just save when you're ready to save. Let's take another case. I want to create a group named Friends, and this group has two persons. I don't have many friends, I suppose.

So Address Book, new group, get the share instance of the Address Book, create a new group object. Set the name of this group, a big group named Property Friends. And now I add my two persons into this group, Add Person, Person 1, Add Person, Person 2. And I can add this group to the database. I assume here that the person were already in the database, the existing persons. And I can save my changes. So as you see, this is very, very simple.

Now we're getting to something more interesting. More often, what you want to do with this kind of database is do some search. You want to search for something. So the key API here is in the Address Book class. It records matching search elements, and it will return an array of all the records that match the search elements. So what are those search elements? So let's talk about search elements.

The search element is a building block for query. For example, you want to say, "Find all the people that live in New York and have a Mac.com email address." That's your query. So you will need three search elements. The first one is, "The home city of this person is called New York." The second search element would be, "Find all the email that contains Mac.com." And the third element is a combination of those two. I want the first one and the second one. So as you can see, you can create little blocks and combine them into one bigger sentence.

So how do you create a search element? There are two methods to do that. There are three, but I will talk about the third one later. The two important ones are there are class methods on AB_PERSON and AB_GROUP. So if you want to search people, you're going to create a search element using the AB_PERSON API. If you want to create a search on groups, you're going to create a search element using the method on AB_GROUP.

[Transcript missing]

The property, I want to search emails, I want to search addresses. He has a label, I want to search home, work. He has a key. The key is, for example, for the multi-dictionary I talked before. I want to search only on the street, or on the zip code, or on the country.

And he has a value. I want to search for John, for Paul. And the type of comparison, contents, greater or less. We have a dozen of different types of comparison. So let's take an example. It's much easier to understand with an example than anything else. So I want to search for all people that have John as first name.

So I declare some variable address book, a search element, and the NNS array where I will have the result of my query. Get the shared address book. And here I'm going to create a search element. So I'm looking for people, so I create a search element by calling a method on any person. I'm searching for first name. So, properties, first name. First name is a single value. There is no label, there is no key. So I pass nil for the label, nil for the key, and I'm searching for John.

And I want to find an equal case and sensitive. Maybe I didn't type the upper case, so I just want to be sure to find everybody with the name John. And I call Address Book, record matching search element, and it's passing my element I just created, and it's going to return an array containing all the people who have John as first name.

Let's take another example. Search for all people living in New York. Same thing. All people, so I create a search element on AB Person. I'm searching for an address, so the property is KAB Address Property. I say live in New York, so I want to only search for the label "home." And I say New York, so the city is a city.

So the key here, because the street, the... An address is a dictionary. The key is address city key. And the value is New York. Case and sensitive, also. I may have not typed it correctly. And address book, record matching, search element, and I get a result of all the people who live in New York.

So now if you want to make more complex search, you want to combine them, there is one other way to create a search element. And there is a method on absearchelement itself where you can combine sub-elements. The function is called search element for conjunction. So the conjunction can be and and or, and you pass an array of children. So let's see how to do that. So let's say we want to search for all people that live in New York and have a mac.com email address.

So declare a bunch of variables, and it's three search elements. I need an array to put all those elements into the children for the query. I need the result array for the result. Get my address book shared instance. And let's build those elements. So I'm searching on AB Person. This is the same as before. Property address, home, address city key, New York, case insensitive. For the second one, the email.

I didn't say if it was the home email or the work, so I just used KAB email property. I passed "nil" as a label. I want to search for every email. I don't care about the label. Email is multistring, so there is no key. I'm searching for mac.com, and I'm searching for content substring. I could also search for "ends with mac.com." So now I have my two search elements, one for the city, one for the email, and I need to combine them.

First I create an array of those children, put them together, and then I'm going to create the third element, saying, "I want to do an 'and'," and this is all the children, the two previous elements I've created. And I call Address Book Record Matching Search Element. And it's going to return the result of your query.

That's all for searching. The next topic is notification. The Address Book Framework provides two notifications. The first one, KAB Database Change Notification, means your process changes the database. So you may want to listen to this notification or not, depending on how your application is built. You may not want to listen to it because your application is making the change, so you may change your UI. You know that you've changed it, so you don't care.

The other one means KAB Database Change Externally, means some other process has changed the database. You better listen to this one because you may have to reflect those changes into your UI. New people were created, new people were deleted, so you have to do something. So those are the two notifications. One for, I've changed my database, someone else has changed the database.

As I said also before, you can extend the property set. So we provide a set of standard properties, but you can add your own properties if you want to add the height of someone or anything you want. Be careful with the property name. It has to be unique, so give him a unique name, com.apple or com.mycompany.somethingelse.

So how do you add properties? You can add properties on either AB person or AB group, whichever. By the way, when I say the name has to be unique, it has to be unique in AB person. You can have first name in person and first name in group. They are fine. Just for an AB person, all the properties have to be unique. For an AB group, all the properties have to be unique. Properties are added and removed in batch.

It's more efficient for us. You basically say add properties and types, and you pass a dictionary. The dictionary is the key, is the name of the property, and the value is the type of the property, integer, real, date, et cetera. We return the number of properties we were able to add.

If you give me 10 and I return 9, it means I wasn't able to add 10. You can also remove properties. Just pass me an array of the names of the properties We can also give you the property for, for example, AB Person. If you call AB Person properties, you're going to get an array of all the properties on Person. And you can ask for the type of a given property. So if you give me KAB first name property, I'm going to return you a string.

So I'll show you Objective-C APIs, but we fully support C also in this framework. So if you are a Carbon developer, you can use the Address Book Framework using the C APIs. They are one-to-one matching between the C APIs and the Objective-C APIs. This is exactly the same APIs, just the syntax is a bit different.

Classes, instead of being objects, they are represented by OPEC type. So we have AB Person Ref, AB Group Ref, AB Multi-Value Ref. This is very, very similar. It's exactly the same thing as the other CF, the Core Foundation APIs. So you have the same rule, the same coding convention as any other Core Foundation framework.

So, give an example here, which is the example of creating a person called John Doe. Something, I AB get shared address book, which return an address book ref. I create a person, AB person create, give me a person. AB_RECORD_SET_VALUE passing the person, the record in this case, the CFSTR for the name, and the KAB_FIRSTNAME property.

Set the value for the last name. And I can now tell the address book, add this record. And don't forget to release the person. You've done a call, AB create, create bumps or fcounts, so you have to release your object. So I have to do a CF release person.

We also fully support AppleScript. I think there was a demo during the-- I think in this same room, there was a demo of AppleScript support in Address Book during the AppleScript session. So you have full access to all the Address Book API-- Address Book Framework API with AppleScript. This API is accessed through the Address Book application. So you have to tell application Address Book.

But you have full access to all the API. So this is an example. I want to list all the emails for a person. So tell application address book. I say set all people to people. People will return a list of all the people in the database. And now I'm going to repeat with a person in all people.

And I'm going to say set all emails to emails of a person. So I'm going to get all the emails of a person. And I'm going to print the value going through every email in a multivalue, log the value for this email as a string. So that's it. Same process, different syntax, but all the APIs are available.

So what I want to do now is give you a short demo address book framework, the address book application. So in the Jaguar, we have a brand new address book application. I'm sure you've seen it. It was demoed at the keynote. It was demoed several times during this conference.

So this is basically the address book. We try to have something that looks like a card. You can switch cards. You can add notes at the bottom. And so you can very much like iTunes or any other application. You can do a quick search, so The demo before was to search for Fred Anderson. I type Fred and I get, it says here I have four Fred in my database so I can continue. And it says there is only one Fred Anderson. So I can also switch to go around all my different threads.

So, a very well-known user interface, very easy to use. Something we try to do is that we have also those rollovers here. So for example, from a phone number you can get a large type, so you can read it on the screen. You can also, from an email, you can send email. If it's a mac.com email, you can visit the home page of this person.

You can open the ID from this person. If you have a URL, you can go to the website. And also you can, if you have a street address, you can also get a large type. Henri Lamiraux I don't know if I'm connected. You can get a map of this address.

So you have great direct access to all the information in this card. If you want to change something, you can always add a note to a card. So you can always type inside the card. And it's immediately indexed. So if I type my name, I can see it.

And I go to some other card and I search for Lamiraux. He found two cards. One is here, and one is my real card. So immediate indexing of every information you're typing. If you want to change something, you go to edit mode, and you can edit in place your information. You can add a new phone.

You can add a new email if you want, etc. If you want to delete, just go there, delete it, and it's gone. And once deleted, we only show you the information that's available. You can also have access to groups. So here I'm looking at everything in my database. So I'm listing everything contained here, but I can also get access to groups.

If I want to move someone in a group, it's easy. Let's take Bruce Arthur, move it into the emergency. So just moving, dragging and moving, you can easily... Bruce Arthur is here. Notice here that I have a subgroup. So emergency has a subgroup which is doctor. So a very simple user interface based on the Address Book Framework, and a lot of applications in Jaguar will be using this Address Book Framework. framework.

So as I said, Address Book Framework will be available in Jaguar. It's a brand new framework available in Jaguar. And we really stress if you have anything to do with addresses, with email, with phone, we really want you to use it because it's going to give our user a much better user experience. Everything will be in one place. You can get the MiCard so you can get access to shipping addresses. You can have access to a lot of things. You can have access to the image of a person so you can display that in some other application.

It's really an important piece for a better user experience, centralized information. And as I said, it's used extensively in Jaguar, so you want your application to play well with all the other applications on the system. And I would like to invite John Gilenzi, who is the person to contact. Monsieur John Gilenzi.