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: wwdc2007-207
$eventId
ID of event: wwdc2007
$eventContentId
ID of session without event part: 207
$eventShortId
Shortened ID of event: wwdc07
$year
Year of session: 2007
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2007] [Session 207] Integrating...

WWDC07 • Session 207

Integrating iCal Events and Tasks into Your Application

Leopard Innovations • 36:29

The Calendar Store framework, provided in Mac OS X Leopard, is a simple interface to a user's iCal data. Using the Calendar Store, applications can read, create, and modify events and tasks with just a few lines of code. This hands-on session explores the ways your application can use calendaring to keep users connected, even when your application isn't running.

Speakers: Scott Adler, Matt DiMaggio, Matt Shepherd

Unlisted on Apple Developer site

Downloads from Apple

SD Video (248.2 MB)

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Hello, and welcome to Integrating iCal Events and Tasks into Your Applications. I'm Scott Adler. I'm from the iCal engineering team, and today we're going to show you all the ways that you can take advantage of the Calendar Store framework to integrate events and tasks into your apps.

So first we want you to say hello to iCal in Leopard. We are the iCal team, and we've made a lot of improvements to iCal, just the screen itself is a little different, but we've added a lot of new features. One of the big things that you might have seen already is we have a new Inline Inspector so your data is right there with your events.

It comes up in different modes depending on whether you're editing or not, and we also have new ability to contact servers so we have the iCal server built into the Mac OS X server and you can get things like availability of other attendees to your meetings and scheduling support. And there are many other new features in iCal but those are just a couple of them. But what we're here to talk about today is what's behind iCal and that is the Calendar Store framework.

So iCal is actually built on top of what comprises the Calendar Store framework. But what we'd like to really talk about now is -- one second and my slide will change, there we go -- is other applications on the system that also use the Calendar Store framework. So mail, you may have seen demos of mail doing things like notes and in your notes you can create to-dos, and there's actually an Inspector for those to-dos. And that all is built using the Calendar Store framework. Now there's some other things in mail you might not have seen yet.

You can actually find dates and times of events and things in your mail messages and automatically create things that go right into iCal that's also using the Calendar Store framework. You may have seen the iCal widget. It's one of the default widgets that comes up on your screen when you bring up Dashboard and, if you click on it, you get your agenda for today and that is all done using the Calendar Store framework.

So the real question is why should you use Calendar Store? Well, we have a couple different answers for you. And the first one is probably the most important and that's we want to do all the heavy lifting for you. You don't really want to have to deal with things like complex recurrence patterns. If you're getting today's agenda, you don't want to have to go through and figure out is today election day, is it the second day in November or, you know, is this the day -- or when is Thanksgiving and all those kinds of things.

You don't want to have to do all those complex recurrence expansions that can be very complicated logic. You'll see more about recurrences later in the talk, but if all you care about is one range of time, we'll figure out what the occurrences for those events are on that day or on that range of days and you don't have to worry about what's going on behind the scenes.

We also take care of all the persistence and all the fast queries that you want to do. So we do this by using Core Data behind the scenes and you just create predicates and queries and we take care of it all for you. You get all your results back fast, and also we take care of all the persistence of laying everything down on disk for you, you don't have to worry about any of that. And also in getting that you automatically get Spotlight indexing. So, if your events go into the Calendar Store, it'll automatically be indexed by Spotlight, you'll be able to get all those great features that Spotlight gives you.

So another reason to use Calendar Store is you want to help your users get organized. Organized users are -- are much more productive and they want to be organized and you want to help them do that. So how can you do that, one is use system-wide alarms. You put alarms into your events, into your tasks, and they'll come up in the system-wide alarm service, so your app doesn't even have to be running.

Let's say you have a list of tasks, they have due dates, you throw alarms in there, they close your application. Two days later when their task is -- has the alarm that comes up that has to remind them, it will automatically happen. You don't have to worry that they haven't run your app in a couple of days.

So integrating your application into their lives, what we mean here is a lot of organized users will have what they need to do that day, what events are coming up. They might be using iCal. They might be using some other utility that another developer here in the audience has written to look at what they're doing that day or that week, and you want your application to be right there up there with everyone else saying here's what you need to do today, here are events you did, if they need to look back at what they did last week. Say you're a billing application and you want to see when you billed people for hours you were working, your data will be right there along with everyone else's data.

And also Calendar data is everywhere, so you might not realize that your application has calendar-specific data, but just the fact that they're using your application at a certain time might be something that you want to keep track of. Just the fact that someone in a chat message said, "I want to go to a baseball game on Friday," maybe there's something you can keep track of.

Even in this chat message up here you can -- you can see that it has a time that I chatted with that person and I might want to keep track of I had an event of chatting with that person, so I could create Event Data that might be important to that user.

So the final reason, of the ones I'm covering today, is getting your data everywhere and there's a bunch of places that users like to see their calendar data and, if you use your application and build it on top of Calendar Store, you'll already get into these places we talked about -- inside of iCal, inside of the widget, inside of mail, you know, the tasks will end up inside of mail but there are other places that use Calendar Store and use the calendar data on the system that you may not have even realized.

iPhoto creates calendars and you can get calendars ordered and shipped to you, and you can put events on those calendars and, if your data is in Calendar Store, the user and iPhoto can decide, okay, am I going to put that data onto my calendar? And then there's going to be many other applications. We have lots of developers here, hopefully, you're going to be using Calendar Store and your data will end up in those applications too if it's applicable.

So the other thing -- one of the other ways you get your data in other places is using Sync. And so your local data on your machine, your data that's not on a CalDAV server or not on the iMap server will be synched so that your data goes into Calendar Store, gets into the Sync database, and then ends up on all the devices, and on .Mac, anything that can support calendaring information. So your data without you having to do anything it goes right into sync and it will end up on these devices so long as it's local data.

So I also talked about CalDAV a couple times. So CalDAV is an open standard; it's supported by lots of different organizations. We have a CalDAV client and a CalDAV server. The iCal server is built on top of CalDAV and this is built for small business and enterprise scheduling and group calendaring, and iCal now supports it.

Well, your data, if you put it onto a CalDAV calendar, what happens is your data goes from the Calendar Store into iCal, and then iCal communicates to the iCal server and this arrow is a little misleading. You can see the arrow goes this way, well, the important thing is that the arrow goes the other way too. So any new updates whether it's from a handheld device talking through another CalDAV client getting onto the server will end up back into iCal and then your application will get notified that data has changed.

So you get to use CalDAV right our of the box. If you're talking to a CalDAV calendar and putting your events on that calendar then all your data will end up on the CalDAV server for free. There's nothing you have to do other than just use the Calendar Store APIs.

So I've talked about a lot of different reasons to use the Calendar Store framework. There's lots of applications that support it. It's an easy way for you to get calendaring data and through a very simple Objective-C API. And now to discuss the inner details of that whole API, I'd like to bring up Matt DiMaggio.

( Applause )

Thank you, Scott. So just to give you guys an idea of what I'm going to be talking about today here's a roadmap. I'm going to start by going over how to read calendar data. Then we'll look at the notification model, and we'll move on to writing calendar data, and we'll finish with some common questions.

And the way that I'm going to be discussing reading calendar data is by exploring all of the various classes in the Calendar Store framework. I think a very easy way to understand these classes is by start by looking at iCal, so this is Leopard iCal. And moving left to right you'll be able to see that a lot of the things in UI correspond directly to things in the framework, so the list of calendars is going to be represented as CalCalendars, events are CalEvents, tasks are cal -- I'm sorry, to-dos are CalTasks, and just a small note on that wording there. What appears as a to-do in the UI is just a task in the API.

It's the same object, we decided to use task just to actually make the capitalization and the spacing a little easier but it's the same thing. And in addition to those classes there are a few more classes that you can correlate the things inside the Inspector. You can see this is a reoccurring event, the recurrence data will be encapsulated as a CalRecurrence rule, attendees are CalAttendees, and alarms are CalAlarms.

So that's what the data looks like in iCal, and kind of through the first part of this presentation we're going to show you how to go from here, where it is in iCal, to here, where you can show the data in your application. And I'm going to start by discussing actually the one class that wasn't in that screenshot of iCal, and that's CalCalendarStore.

Now CalCalendarStore is really the superclass or not superclass, but the main class of the framework. This is where it all starts, and this is the class that you're going to use in order to get calendars, events, and tasks. It's very similar to the ABAddressBook class, if you've used the AddressBook framework.

And here's some methods on CalCalendarStore. As I mentioned it's similar to ABAddressBook. There's one kind of default or shared instance. In addition to that you can get an array of all the users' calendars. You can get an array of selected events, and you'll notice that that method takes a parameter.

Now I'll go much further into detail about that, but for now I'll just tell you that -- that parameter is a predicate, AddressBook framework uses AB search elements we decide to use NSPredicate but it's the same idea. It'll tell us exactly which events you want, and we'll pass you back an array with those events. And finally, a method to get tasks, again, using NSPredicate.

So as I discuss these classes I want to show you kind of a data model of how these things fit together. That's the CalCalendarStore class. The next class that I'll talk about is CalCalendar. As I said before that's one of the classes that you'll be able to access directly through the CalCalendarStore class.

Now CalCalendar has six properties. Just as I kind of correlated things in the UI to classes in the framework, I'll correlate a lot of the things in the UI to properties of CalCalendar. So what you see at the bottom there is a screenshot of what comes up when you try to edit a CalCalendar in Leopard. There's a sheet, that sheet allows you to change the title of the calendar. It's name. There's a color property, and there's another string for the notes on a calendar.

Now in addition to those properties, there are a few more that don't correlate directly to things in the UI. We've got a type string that will be one of a few statically declared types that we specify. You can see those in the documentation. That will be things like a CalDAV calendar or a subscription calendar or just a plain old local calendar that's what the user starts with by default.

There's a bBoolean for whether or not the calendar is editable and this refers not only to whether you can make changes to the calendar through the API, but whether or not you can make changes to the events or tasks on the calendar. On a subscription calendar, for instance, one that's all the U.S. holidays, in iCal's UI you're not able to add or remove things from that calendar. The same thing is true in the API. And, finally, there's a UID. This is just in case you need to store a reference to that CalCalendar later on, make sure you have the right calendar.

Looking back at the diagram we talked about CalCalendar; next class I'm going to look at is CalEvents. A CalEvent is actually one of two subclasses of CalCalendarItem. CalCalendarItem encapsulates all the things that a CalEvent and a CalTask both have, all those properties. That includes things like the title, the calendar that will be a CalCalendar, of course. CalCalendar items can have one or more alarms. There will be an array of CalAlarms, URL, notes, and a date stamp for the last time that event or task changed, and finally, a UID just like in CalCalendar.

Now, in addition to those, CalEvent has properties of its own. Events can have locations, a Boolean for whether or not it's an all-day event, start date, and end date, and, if it's a repeating event, it'll also have a CalRecurrenceRule. If this is a one-time standalone event, then that field will be nil. And finally, an array of CalAttendees.

So I told you what CalEvents look like, but I haven't told you how to get CalEvents yet. Well, I think it's a pretty simple process. There's a few steps. What you're going to do is start by getting the default Calendar Store. You're going to use that Calendar Store to create a predicate and then you'll pass that predicate back to the Calendar Store where you'll get an array of events.

So here's a call to get the default Calendar Store. And this is a method that we haven't seen. What we have here is a class method on CalCalendarStore that's going to return an NSPredicate. Here it's eventPredicateWithStartDate, endDate, and calendars. What you'll do for this particular predicate generation method is pass in the start date that the date range of events you want and the end date, and then the list of calendars that you want to query events from.

And in this example we're just passing the array of calendars directly back so that will include events on any of the users' calendars. And finally, we call eventsWithPredicate passing the predicate we just generated. And that will give you all the CalEvents that you requested. The last class that you access directly from the CalCalendarStore class is CalTask.

Now CalTask is the other subclass of CalCalendar item so it's going to have all the same fields that CalEvent had, things like a name, notes, the CalCalendar and a UID. In addition to that it has some properties of its own. There's a Boolean for whether or not the task has been completed.

If it is completed, there will be a completion date. There will be a priority and that's just in your enumerator type that's going to be something from zero to nine that's all CalPriority is. And finally, if the to-do or task has a due date specified, there will be a dueDate property.

So for events I showed you an example where we passed in a start date and end date in a list of calendars in order to get the array of events that you wanted. For tasks it's a little bit different. We don't have a strict start date or end date per se. You're still going to use an array of calendars instead of a start and end date you'll be able to key off of things like whether you want completed tasks, whether you want tasks due before a certain day.

And just to briefly show you the predicates that you can use to generate -- I'm sorry, the methods you can use to generate a predicate to give you a list of tasks. But even better than showing you this code up on the stage or showing you snippets is seeing a demo of how it all works. Matt Shepherd is going to give you a demo.

( Applause )

Thanks.

Okay. So what we have here is -- what we have here is the Checklist sample app, so I'll just give you a quick look at what this is. So it's just a list of tasks. These are the same tasks that you would see in iCal. So, now all that's really in here is a single controller -- start with the interface -- now what's in this class is basically a reference to a tableView and a cached array of tasks. And this Controller will also implement the DataSource for this table.

So take a look at the implementation on awakeFromNib what we do is we go ahead and grab an instance of the CalCalendarStore, from that CalCalendarStore we get an array of calendars, and then we ask for a predicate for all the tasks in that array of calendars. So we're going to load everything, every task in iCal, by calling tasks with predicates. And the important thing that we're going to do in this implementation is we're going to retain the array of tasks, and we're going to cache that in the Controller, and then, once we have that array, we're going to tell the table to reload.

Now this is a just a simple DataSource implementation so from that cached array we know how many tasks there are, and then I've cheated a little bit and I've named the table columns -- the identifier of those is the same as the properties on the tasks -- so in this case completed and title.

So by doing that I can grab the value and then I can sort based on the -- the various column names. So just by doing that I'm able to, you know, populate the table and show you the same data that iCal sees. That's all there is to it.

( Applause )

  • So we've looked at three kind of primary classes in the CalCalendarStore framework. The next class I'll review is CalAlarm, and this is something that events or tasks could have. So CalAlarm has a few properties. The first property is probably the most interesting. That's the action and that could be any sort of the -
  • any of a few values depending on what the user selected in iCal.

You can see there in the pop-up that's what it looks like in iCal, and there's a statically declared string for each of those. After you've specified that the alarm is a message with sound, there would also be a string for the name of the sound. If it's an email alarm, there will be a string for the emailAddress, and, if it's an open file or a run script alarm, then you will be able to access the file URL of the file that's being opened or run.

In addition to those, all CalAlarms will have one of two types of triggers. Now in this example the event is -- the alarm is specified to fire five minutes before the event starts. We call that a relativeTrigger. The trigger time of the alarm is specified in relation to the start time of the event or the due date of a to-do. The other possibility is that the trigger is specified with an absolute date. User said they want the alarm to fire exactly at a specific date and time, and in that case there would be an absolute trigger which is an NSDate.

The next class is CalAttendee. CalAttendee just has a few properties. There's a commonName that will be the name of the attendee that shows up in iCal. There's an NSString for one of a few -- a few possibly -- a few possible statuses for the attendee depending on whether or not they've responded to the invitation and accepted or declined it.

And there's an NSURL for the address of the attendee. Now in Tiger it was possible to invite attendees to an event and the way that happened usually was you'd click send and it would send an email to them, but it always required an email address. So in that type of event it's still possible in Leopard, if you have an attendee like that, the address will be a mailto.

In Leopard it's also possible to have a meeting on a CalDAV server, and if the meeting is on a CalDAV server, the server admin, or I guess, the server software could specify any sort of URL as the address of an attendee. Now, if you actually go and try this on a few servers you'll find that many times the address on a CalDAV server is also a mailto address, and I believe that's what we do for Mac OS X server as well.

And the final class in this data model is CalRecurrenceRule. So this one is a little trickier. We think that CalRecurrenceRule will be most useful to you as developers when you want to create an event that has a recurrence pattern. Say you want to create an event that happens every two weeks or happens every Monday through Friday. In that case you'll create a CalRecurrenceRule and you'll attach it to the event, and that's how you create a repeating event.

Now, if you're really only concerned about showing repeating events in your UI -- say you want to show the events for a week and you want to make sure you that you show each instance of a repeating lunch meetin -- that's kind of what that screen shot at the bottom is trying to capture -- if that's all you want to do, you don't need to be aware of CalRecurrenceRule.

What'll happen is you'll request all the events in a week, you'll get a start date and an end date, and then we'll create a CalEvent for each one of those meetings. You don't need to be aware that the CalRecurrenceRule is Monday through Friday. We do all that behind the scenes for you.

Now, if you've played with iCal, you might be aware that iCal let's you specify a kind of rich set of definitions of different recurrence patterns. Everything from the simple like every day or every week to increasingly more complicated things, the first of every month, the fourth Thursday of every November. And in order to give you the freedom to create all those recurrence patterns, iCal uses a lot of different UI.

It's either things for yearly recurrence patterns, monthly recurrence patterns. It can get a little sticky so just as that's true about the UI, we've created a lot of API to provide you with the same level of functionality in the framework. Now I could spend the next 20 or 30 minutes talking about all these different types of constructors and how you specify a CalRecurrenceRule that is the last Friday of every financial quarter, but instead I'll just refer you to the docs. It's very well commented. I think it's very -- it's pretty well thought out and easy to understand. We really wanted to give you guys the same function that's available in iCal, just turns out that it requires a lot of code in order to get that done.

So those are the classes in the framework. Now, let's look at the notification model. So at a very high level, the way this works is that the user will make a change in iCal or another application. That change would be made through the Calendar Store framework, and then your instance of the Calendar Store framework will send out a notification if you signed up for notifications.

Again, this is very similar to the way AddressBook does things. One difference is that there are separate notifications for calendars, events, and tasks. This is nice if you're only concerned about one of those data types because if a user checks off a to-do and you're only showing event data, then you won't get an unnecessary notification. The API also allows you to specify that you want to listen for local notifications that would be changes made by your application or for changes made by any application.

Once you get that notification, you have a couple options on how to update your UI. You can either refresh the list of events or tasks you got, either by creating the same predicate you had before or passing that predicate to the Calendar Store and getting the updated list of events or tasks. And the other option is to dig into the userInfo dictionary that's included in the notification and specifically update the one event or task that changed based on its UID. So if you choose to do it this route, you're going to want to be able to request a specific task from the CalCalendarStore, that's what this method looks like. They say taskWithUID and you pass in the UID of the task. You'll get back the updated task.

For events it's a little bit trickIer. You can see that the call we use includes not just the UID but this other thing called occurrence. Now the story on that is that non-repeating events, that's one-time events that are just scheduled once and don't repeat at all, they all have their own truly unique UID. There's nothing else in the Calendar Store framework that would provide you with that UID other than some very small random chance. Now repeating instances of a single event also have the same UID and that's just the way the iCalendar spec was created.

The way we differentiate between those instances of the same event, the same repeating event, is to give them this thing called an occurrence value, and so here, this lunch -- this repeating lunch event -- all those have the same UID, they all have different recurrence values. An occurrence is one more property on CalEvent.

You'll note that it's an NSDate. That's just because that's what the spec says it should be, the iCalendar spec. We really advise you guys to just treat that as another part of the UID and really kind of keep the UID and the occurrence and use them both to get a specific event from the Calendar Store framework.

I'll move onto writing Calendar Data. And the set of steps that you're going to use in order to make changes to user's calendar data or to add an event or task of your own aren't all that different from when you were reading calendar data. But instead of showing you sample code or showing you some method prototypes, Matt Shepherd is going to come back up and give you guys a demo of how to use the writing API, as well as show you how you can use event notification stuff to update your data.

  • Okay. We're going to take that last sample. I've added a couple of things to handle the notifications as well as writing. So we're still loading the same data. We're still using the same predicate, the one change at the beginning here is that we're going to register it for the CalChange -
  • CalTasksChangedExternally notification. The externally part means that you're not going to receive your notifications from your own app. It's a different notification. You can get that if you want, but in this case not so much. And in the dealloc we will remove ourselves as an observer.

So how do we handle that? So as Matt said you can handle this in two different ways. With this task changed method I can either just reload all my data or I can sift through the userInfo and I can update my cache. So in this case we're going to sift through the userInfo, so we're going to loop through all the deleted tasks which you can get by using this -- this constant.

You can loop through and you can remove those from your cache as we do here based on the UID. So you can do that, and then, when you go through the updated tasks here, you can then -- you can then replace the tasks that -- that have updated. And you do that by checking the UID from the userInfo and then reloading that task using the taskWithUID method and then going through and replacing that as well.

And then in the last case that we deal with is a new task which is signified by this inserted constant, and you can iterate through those and then you can also load those using the taskWithUID and then you can add those to your tasks array. And then -- and then you can reload the data in the table.

So that's -- that's for notifications, but for -- for writing we only have to add really one method down here which is the data source method for setting a value based on an update to the table. So in this case, the only change we're going to make in our app here is we're going to allow the user to set completed.

So the TableColumn is identified as completed, and so that when that changes, all we have to do is set the completedDate on the task and then grab an instance of the Calendar Store, call saveTask, and handle an error. So, if we do that -- and I'll load this one up. So if I check this off here, you see that it shows up in iCal, and as far as our notifications go, if I go and I create a new to-do in iCal and save it -- it shows up in the Checklist.

So there's not a lot of code here and yet we've gotten all access to all the data in iCal, and we're able to make changes and the notifications are handled in iCal for us, so --

( Applause )

  • So one final note on saving changes to events or on saving changes is that for events it is slightly different than tasks, and for the same reason as before because of these repeating tasks -
  • recurring tasks.

You can see that the saveEvent method has an additional parameter and that's the span -- that will be a type CalSpan. This is only meaningful for repeating events. If you're saving a change to a non-repeating event, then it doesn't matter what value you pass in for the span.

For a repeating event you'll want to pass in one of these three values. The names kind of give away what they do. CalSpanThisEvent means that the change you're saving should only apply to this event; CalSpanFutureEvents means it should apply to future events; and CalSpanAllEvents means apply that change to all the events in the repeating pattern.

If you've used iCal and had reoccurring events before, this is very similar to the panel that comes up and asks you, do you want these changes to apply only to this event or to all events or future events? So I'm going to end by going over some common questions that people have about the Calendar Store API, and this probably isn't the first question you thought to ask, but if you're going to be writing any data to the user's calendar it is a question that you'll need to ask yourself and you'll need to answer.

Should I create a Calendar for my app? Well, we believe that users already have their calendar data organized how they want it organized. They've already picked out the colors and the names of the calendars that they want to use, and so what we're telling you guys is that you should ask the user what they want to do. You can see in the example there there's an option to add it to a specific calendar. There's also an option to create the calendar specific to your application if that's what they want to do.

One other note about adding calendars to the framework is that, if you're adding calendars, you can only add local calendars to the user's Calendar Store. If you choose to save an event to a specific calendar, the API does allow you to save events to CalDAV calendars. The next question, this is probably a little more common is, how do I add my own custom data to iCal? Well, there are a few options.

If you have some very basic data, it might be appropriate to put that data in the URL field or the notes field of your event or task. Now there's a big caveat that goes along with that, that if you put data there, then when the user launches iCal they'll see the data that you've put in the URL field or in the notes field, and they'll be able to modify it and so you can't count on that data always being the same.

Another option is to store it yourself and then sync that data up with the tasks or events in the API using UID or finally you can use the Sync Services API. Sync Services API has an iCal schema which you can extend and add fields to. There are caveats that go along with that as well.

If you choose to extend the iCal schema and put your own data into these records using the Sync Services API, you will not be able to access that data from the Calendar Store framework; those are new fields and the Calendar Store framework just doesn't know anything about them. And really we advocate that people use either Calendar Store or Sync Services, but not both. The reason is that it's very easy for your data to get -- for lack of a better word, out of sync.

And that dovetails very nicely into the next question: Should I use Calendar Store or Sync Services? Well, we're really excited about the Calendar Store framework. It's an easy Cocoa way to access your events and tasks, and so, if you have already written the rest of your application in Cocoa, this should match really well with it.

In addition to that, you will be able to access all of iCal's data using the Calendar Store framework that includes mail, to-dos, CalDAV events. Those are records that don't get pushed down into sync. And the Calendar Store framework also allows you to receive real-time notifications as changes are made in iCal or another app.

One really big win to using Sync Services is that it does allow you specify arbitrary custom data and allows you to attach that data to events and tasks. Another advantage is that Sync Services lets you determine what data has changed since the last time your app is launched.

So in closing we're really excited about this framework. We think this is a new technology that's been missing from Mac OS X for a while. We think it's going to allow you guys to do a lot of exciting things, integrate with your user's iCal data, make your apps more useful to your users, and help your users get organized. We're really excited about what you guys are going to be able to do with it. There is an iCal and Calendar Store Lab tomorrow, that's 2:00PM tomorrow in Mac OS X Lab C.