Application Technologies • 22:54
New Leopard Calendaring technologies offer exciting ways to programmatically integrate with the iCal API to obtain, edit, and create iCal's event and task information. Whether you want to simply query upcoming events or use iCal to store all your calendaring data, the CalendarStore framework empowers developers to easily add calendaring functionality. In this session we will explore the APIs in the CalendarStore framework and demonstrate how easy it is to use.
Speakers: Red Dutta, Matt DiMaggio
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it may have transcription errors.
Thank you and good afternoon. This is the Integrating with iCal session, and I'm Red from the iCal team, and just want to thank you all for coming on down. So the team has been very busy working on a new framework for all of you guys. You saw it in action Monday morning with mail to-dos. You could see it takes full advantage of our to-do support with due dates, alarms, priorities, putting in what calendar you want. So it reads and writes to-dos.
As you may have seen on the seed or other demos, there's a new widget that used to be called the calendar widget, which is now the iCal widget, and it takes advantage of the framework to use events. So you've got mail using it for to-dos and the widget using it for events. We'd like to see a lot more applications from all of you guys coming on and using it, and These are just kind of maybe some wishful thinking possibilities. So you're going to hear this a few times that it's easy. I'm saying it now. You're going to hear my teammate Matt say it a few times. We've taken the time to do all the heavy lifting as far as the data storage is concerned and recurrence expansion. So like the widget, if you're interested in just a particular day's worth of events, you We'll do all the figuring out of what events really exist today. You know, Thanksgiving, which is the fourth Thursday of every November, does it fit inside the pattern or not? There's hundreds of lines of recurrence expansion and valuation. We're doing that. The data storage of how you store, you know, events that occur on the first and 15th of every month.
We'll do all that heavy lifting for you. You all should focus on making your apps great and figuring out ways to interoperate with iCal. So the stuff you have in your app will show up in iCal, and then if you want to read it, it'll be there vice versa also. Now, what if you're already, you know, accessing iCal through AppleScript or Sync Services? Well, don't worry about it. We're not touching it. It's still there. It'll still work. And if you're asking yourself, so which way should I get at the data? Should it be through AppleScript or Sync Services or the new framework? I'd ask you to think about the Address Book framework a little bit, because it actually has all three methods, comparable methods, of interacting with it. And you have to think about your data interaction and how you want to really interact with Address Book. The same thing would be true about Calendar, the Calendar Store framework.
So one other thing. This is a very big, large, extensive framework, and we believe we've done a really good job. We know we can make it better, and we want your feedback. So tomorrow at Mac OS X Lab C, we're all going to be there at 3.30, and we'd like you to come down, bring your PowerBooks, bring your code, try it out. See what works well. See what needs a little polish.
Come and just meet us and talk to us. Tell us what you need, what you've been doing with iCal, and see how we can try and make things work as best as we can for all of you guys. So without further ado, I'd like to introduce Matt DiMaggio, and he'll go into the framework in detail. Thank you.
Thanks, Red. So my name is Matt. I'm excited to be here to talk to you guys about the CounterStorm framework, which is, as it says up there, a new way to access iCal's data. So just to give you a kind of road map of what we'll be talking about today, first I'll go over reading calendar data and look at all the various classes in the API. I'll move on to the notification model and finally conclude with writing calendar data, going into things you need to be aware of when doing that.
So who is this API for? Well, anybody who wants to access iCAL's data. This could be reading and writing data, as Mail does, or just sharing data, like the new iCAL widget. I think the easiest way to understand the classes in this framework is by correlating them to things that you're already familiar with in iCAL. So you'll notice in the list of calendars there, those things are all represented as CAL calendars. Events are cal events. To-dos are cal tasks, and so on.
And what we'll be doing throughout this talk is kind of giving you an example of how to go from here, where the data's in iCal, to here, where the data's in your application. So the first thing we'll talk about, the first class, is Cal Calendar Store. This really is where it all starts. I think the easiest way to-- one thing that I remind you of is AB Address Book. This is the class where you'll be getting access to the three main data types in the framework. That's calendars, events, and to-dos. And if somebody's looking at that and thinking, to-dos, hmm, there shouldn't be an apostrophe there. Well.
We talked about that and talked about maybe writing it as "to-dos" without an apostrophe. That doesn't look right, so maybe there's not a space. We actually spent an inordinate amount of time on the team going over how this should be spelled and written in documentation. And so what you'll find within the API itself is we do not refer to "to-dos." Instead, we call everything "tasks." So... I'll use the two terms interchangeably while I'm up here, but they're the same. A to-do in iCal is a task in the API. So as I said, Cal Calendar Store is your main place to access data. It contains all the information that you'll see in iCal.
And here are the principal methods to access that data. So default calendar store is a class method that gives you access to an instance of the calendar store. And then you can make calls on the calendar store to get a full list of calendars or to get-- there you go-- and that'll return an array of Cal calendars. An array of events using a predicate, which we'll talk about, that returns an array of caliphants. And of course, tasks also using a predicate.
So I'll kind of be building this diagram as I go through, just another way to kind of understand the classes and the relationship with each other. That's the Cal Calendar store. The first data class that we're going to look at is Cal Calendar. Again, this is exactly the same thing we would be seeing in iCals UI.
So here are the main methods on Cal Calendar. There's the color, which is the same as the color that shows up in iCal. And the title, which is again the title. And there are two more methods here that are also pretty useful. IsEditable would tell you whether or not you can make changes to events and tasks that appear on the calendar. So example of events that the user can't make changes to, and that you won't be able to make changes to using the API, are things that appear on a subscribe calendar, on iCal's birthday calendar, or on one of the CalDAV calendars, which you probably heard about in earlier sessions. And finally, a UID in case you need to store a reference to that calendar for some point in the future. All right. That's the first data class. The second one is CalEvent.
Now, CalEvent is actually a subclass of CalCalendarItem. This is the only inheritance we have in the framework. And CalCalendarItem contains things that are relevant to events and tasks, or to-dos. Those things include the title, the calendar, which is returned as a CalCalendar, the class we just reviewed. Alarms, which is an array of CalAlarms, which we'll look at later, and the URL and notes that are associated with a given event.
And CalEvent itself has event-specific data. There's the location, whether or not the event is all day. The rule is stored as a CalRecurrenceRule, yet another class in the framework. And finally, attendees. That's an array of attendees. Those are represented as CalAttendees, the final class we'll be looking at.
So using those three classes, Cal Calendar Store, Cal Calendars, and Cal Events, we actually have enough information to show the events as they appear here in the iCal widget. Now one other point I want to discuss before we look at some sample code is that the events you see here represented in the widget aren't all of the events that are in the user's calendar. These are events that begin on a specific day. And you're going to define that using the API with a start date and an end date. So there's two pieces of information you need.
The third one is a list of calendars. And here in the widget, we're actually showing events from all the calendars. But in your app, you'll be able to show events and tasks from only certain calendars, just like iCal does. So we take those three pieces of information-- the calendars, the start date, and the end date-- and roll them up into a single NS predicate that you'll be able to use to query for a list of events.
And here is some sample code. Start off by getting an instance of the Cal Calendar store. and kind of create a predicate using the three pieces of information I just discussed-- the start date, end date, and a list of calendars-- and then get all of the events. That's it.
So if all you wanted to do is look at the events that are in the AI Cloud widget like you saw there, then we're done. You can actually get onto the Safari and WebKit session going on down the hall and get some more information. For the grizzled veterans that are looking for a more thorough discussion of the API, we'll move on to the next data class, which is CalTask.
All right, as I mentioned before, CalTask is a subclass of CalCalendarItem. So it has all the same things that CalCalendarItem has for CalEvents. And these are calendars, the URLs, et cetera. And then CalTask itself only has a few specific fields. There's a Boolean for whether or not it's completed. an enumerated type, calPriority, for how high the priority of the event is, and a due date, stored as an NSDate.
So when we talked about events, there were three parameters that we used to kind of describe the events that we wanted-- the start date, the end date, and the list of calendars. Now with tasks, it's a little different. We're still using an array of calendars, but instead of a start date and end date, you'll use completed, whether or not it's completed, and when it was completed, and the due date-- again, whether or not it has a due date and when that due date is.
The sample code is very similar. Get a copy of the calendar store, create a predicate using the array of calendars as discussed before, and then ask for the tasks. Again, that's it, three lines of code. Now because the due date and the completed date are different and a little more complicated than with events, the predicates look a little different. And you'll notice that when you look at the predicate extensions in Cal Calendar Store. There's more ways to query for tasks and a few more predicates.
All right, so these are the three main data types that you'll be accessing-- CalCalendars, CalEvents, and Caltasks. The next three classes are all accessed by way of those data types, starting with CalAlarms, which isn't a data type you can access from any CalCalendar item, specifically a CalEvent or a Caltask.
So CalAlarm's, the first method is action, and that's going to return one of a few different constants that we've defined in CalAlarm.h. That's due to the type, whether it's a sound or an email alarm. And then depending on the type of the alarm, there will also be accessors you can use to get a path to the sound or get a string for the email address that you're sending an email reminder to. And finally, a URL, and that's a file URL that is used for both the open file and the run script alarms. The final two things here, relative trigger and absolute trigger, have to do with when the alarm is set to go off.
If that's defined in terms of the start time of an event or the due date of a to-do, relative to those times, then it would be defined using an NS time interval. That's the relative trigger. If it's set to go off at a statically defined time, an absolute time, that'll use absolute trigger and return as an NSDate. Next up is Cal Attendee.
This is also something that's returned in an array from CalEvents. So Cal attendees have a common name that'll be the user entered string for the name of the person that's attending the event. A status is defined as a static string in the header file. And that's going to be whether or not the attendee has replied and accepted or declined the invitation.
and an NSURL for the address. Now, in the kind of old-style iCal events-- these are events that live on a regular iCal calendar-- that would always be an email address. And since it's returned with an NSURL, it's a fully qualified email address and I'll have mail to it at the beginning of it. The reason that we're using NSURL here instead of returning it as an NSString is that in the case of a CalDAV calendar, this is a calendar that lives on a CalDAV server, you might not have an email address. Instead, you'll have a URL that refers to that user on the server. So here we've got a fully qualified URL that just points to that user's record on the server.
All right, and the final class is CalRecurrenceRule. Now, one thing I want to note here is that this class isn't really necessary if you're just displaying data. If you're just showing the event that happened in a day, iCal does the recurrence calculation for you, and it will tell you automatically whether or not the event belongs in that day. So this is really only if you're adding recurrence data or editing recurrence data on a current existing event.
So CalRecurrenceRule, all right? Every recurrence rule has a recurrence type, and that'll be the unit of time that the recurrence is defined with, whether that's daily, weekly, monthly, or yearly. It'll also have a recurrence interval, which is, simply put, how often the event repeats. So an interval of one means that it repeats every day or week. An interval of two means every other. Three is every third, and so on.
and a recurrence end, which is defined with a separate little class that we've tucked in the cal_recurrence_rule.h. And for a repeating event that's never set to end, the recurrence end will just be nil. This data type is only used when you've already got the end of a repeating event defined. And then if it's defined in terms of a number of times that the event repeats-- And you'll see it defined as-- it'll be returned as an unsigned integer for the number of times that it repeats. And if it's set to stop repeating after a certain date, it'll just be an NSDate.
So then depending on the recurrence type, there are some other methods you can use to access data about the recurrence. These are methods that return arrays of indices on which the event repeats for custom repeating events. So in this example, it's written an NSArray that explains which days of the week an event might repeat.
And in this example, where an event repeats Monday, Tuesday, Wednesday, that would be an array of integers. That would be 2, 3, 4, 5, and 6. Days of the month is the same idea. It's indices for the days of the month that a custom event might repeat. Same idea for weeks of the month. And finally, months of the year.
So that's all the classes in the framework. Next thing I'm going to look at is the notification model. kind of a high-level diagram for what's going on. So when the user makes a change in iCal, or iCal gets a change via sync, or even over another application using the API, that change is made with the Calendar Store. And there is something in the background. The instances of the Calendar Store will talk to each other. So the one that they're using will talk to the one that you're using. And the one that you're using sends off a notification to you. This is a local notification. You won't sign up for distributed notification, a regular notification with NS Notification Center.
And this is similar to the way that it's done in Address Book. You'll be able to use separate notifications for each of the main data types. So if you only want to know about changes that have to do with events, you can just get notifications for those types.
And within the notification itself, there's a user info dictionary. So you can either see that there's a notification and just refresh your data set, kind of using the predicate that you built before. Or you can look at the notification itself, try to the user info dictionary, and get the UID for the records that have changed. Now, we haven't really talked about UID yet. For Cal Tasks, it's very simple. Each task has its own UID, and you can actually access each specific task with a method from Cal Calendar Store called TaskWithUID.
For cal events, it can get a little trickier. Non-repeating events-- these are just one-off events that happen once in the user's calendar and don't repeat-- they all have their own UID. Now, an instance, all instances of a given repeating event, it's an event that repeats more than once, will have the same UID. So you can't differentiate between those with only UID. Instead, we've included another accessor called the occurrence. And each occurrence is unique to the instance of a repeating event. So kind of here's a little diagram to explain that a little more clearly. All of these events, this is one repeating event, they all have the same UID. but they all have different occurrence values.
And so if you ever need to look up a specific instance, a specific Cal event, you can do that on the Cal Calendar Store by passing a UID and an occurrence. Now just to give you a kind of demo of the notification system in action, I'll show you how it works with the iCal widget.
OK. You can see here's my iCal. And when I go into Dashboard and look at the widget, it's only got one event on for today. So I'll go back to iCal. And I'll change the movie event that I had scheduled for tomorrow to be today. And when I do that, a notification gets sent off to the iCal widget, which is listening for notifications. And the iCal widget has the update. The movie appears right there.
So that's notifications. And finally, we look at how to write at-cal data. So the workflow is pretty straightforward. You get the event or task that you want to use. You'll make your modifications to it. And then you just call saveEvent or saveTask in the appropriate method from the Cal Calendar store. So looking at a code example, we have an event. In this case, we're changing its title.
And then we're calling on the Cal Calendar store, saveEvent. There's one other parameter here we haven't talked about, and that's the CalSpan. In this case, we're saying CalSpan this event, only modify this event. Now the CalSpan is an enumerated type. It has several different values. this event, future events, and all events. And this is actually required to be passed in if you're making modification to an event. What we've done here is we've made it necessary for you, the client of the API, to force the user to make a decision as to what the change should apply to.
Just like in iCal when the user makes a change, it says, do you want to apply just to this event or all future ones, and they make a decision there, you're going to be on the hook to do the same thing. In the case of a non-repeating event, it doesn't matter what value you pass here. They're all treated the same.
Okay, so those are the classes we talked about. That's iCal itself. And in closing, we're really excited about this. We think this is a great way to access data. We know a lot of people have been using other ways to access data, but hopefully this will make your lives easier. And we're excited about people using this for things that aren't calendar-specific, which is kind of what I feel like a lot of the things on OS X using iCal data have been. So there will be a lab tomorrow. If you have any questions, we'll be able to help you kind of integrate these calls into your application. and take all of your feedback there.