Leopard Calendar Store

Anyone have any idea if it is possible to access the calendar store via AppleScript or AS Studio?

If not, any ideas how to create a call method to do that within AS Studio?

Thanks.

I am pretty sure that you cannot do it with pure AS; I assume you have found this already? It looks like a multi-step process, and you would need to figure out how to use call method to incorporate it into your application. I am sure that there are some threads here that can help with that; I have only done it once, and that was a loooong time ago. And although this looks terribly interesting, I am not sure if I can figure it out in a short period of time.

Thanks for the link.

I will try to figure something out but it will probably take quite a long time as I’ve only been dragged kicking and screaming into C relatively recently due to trying to push the bounds of applescript. Where do C developers go for complete references to APIs. For instance, if I wanted to get the properties of a class I would have gone to the AS dictionary in Script Editor. What about for C?

Actually, it is Objective-C that you are ultimately in need of understanding. I have been working on learning that stuff for the past 3 months, after spending a few years with AppleScript. Depending on your free time and aptitude, you should be able to get the basics under control in a short period of time. You first need to install XCode onto your machine, if you have not already done so. It is free, and included on the install discs with every Mac. Or, you can download the whole monster from Apple.

The cheapest introduction to XCode and Obj-C is found here. (Free)
This book proves that you can learn Obj-C without first knowing C. Rumor has it that a new edition is on the way in late 2008, early 2009, but the current edition is perfectly fine as is.
Have you found the AS Studio Terminology guide yet? You will find the details of call method in the Application Suite/Commands section.

We have some great tutorials in our unScripted section here at MacScripter, which is currently off-line, but should be back in a week or so.

Hope this helps,

Hi Craig,

That’s a very interesting book, thanks. I read it and was surprised to find Objective-C seems not to be that hard. Unfortunately most of it has to do with just basic number manipulations in C and not too much directly about objects and XCode & Interface builder. Clearly more to read.

For now, really all I want to do is include the CalendarStore framework so that it’s methods can be accessed. It shouldn’t be that hard but I can’t figure it out.

I created an Objective C class “WBCcalStore” that does nothing basically but this:

WBCcalStore.h:


#import <Cocoa/Cocoa.h>
@interface WBCcalStore : NSObject {
}
+(id)newWBCCalStore;
@end

WBCcalStore.m:


#import "WBCcalStore.h"
#import <CalendarStore/CalendarStore.h>

@implementation WBCcalStore
+(id)newWBCCalStore {
    return [[super alloc] init];
}
@end

In IB 3 I added an NSObject and chose the class “WBCcalStore”.

http://kevincathey.com/interface-builder/subclassing-in-interface-builder-3/

The idea was simply to LOAD the framework “CalendarStore/CalendarStore.h” so I could use the methods.

I also tried just adding the framework (as an absolute path) to the project in XCode 3.
http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/Tasks/IncludingFrameworks.html#//apple_ref/doc/uid/20002257-BAJJBBHJ


on will finish launching theObject
	-- idea here is just to load the CalendarStore framework:
        set WBCCalStore to (call method "newWBCCalStore" of class "WBCCalStore")
	set all_cals to call method "CalCalendarStore calendars" of class "CalCalendar"
	set cac to count all_cals
	-- this should show the # of calendars in the store in the text field:
        set content of text field "output" of window "main" to cac
end will finish launching

But I just get an applescript error message “all_cals is not defined”.

So… hoping someone has some better pointers. How can I properly load a framework? Do I even need to create a subclass if it is possible to call methods of classes of that framework directly?

I wish I could be of more assistance. I am just learning Obj-C myself, and I have not advanced upon the path to the point where I can create a class and actually call it from AS Studio.

What I have done, however, is put together a Cocoa application that accesses the Calendar Store frameworks successfully. You can download it here:
CalStore003

It is not fully functional, but it will generate a list of current iCal calendar names, and create a new calendar with a name provided by the user. You will see a popup list and a button to delete a Calendar, but neither of those have been programmed yet. The list of calendar names will be displayed in the Console.

I typically put a lot of comments in the code, so hopefully there is something useful in there for you.

As for adding a Framework to an XCode project, I use the Action button in the XCode toolbar and select Add, then Existing Frameworks… You are then presented with a list of frameworks, and the CalendarStore.framework is in there.

I hope this helps,

thanks craig.

i will study it. unfortunately it won’t build on xcode 3.0 and i get a message that it was built on a version of xcode that is newer (you must be using xcode 3.1 beta?)

Nice find too on the fixes to the SimpleCalendar sample project. That might be productive.

What I can’t understand still is why you can’t somehow just include the Calendar framework in an AS Studio project and use all its methods directly with call methods? Why should any Obj-C actually be needed?

Yes, I have one of the beta releases (I think it is #6) because I wanted to play with the iPhone simulation. It is a long download (and you have to register as a developer), but it is free, and works just fine. I would recommend doing it if you have an Intel Mac, then at least you could see my little baby app run.

I agree with you about the whole Frameworks issue. Probably going to be different in Snow Leopard…

Well… Thanks Craig. I do work on a PPC. (Are you saying that XCode 3.1 is intel only?)

Actually I’m sort of onto something on my own here. But lacking enough of a grasp of Obj-C I’m rather gasping at straws here.

Still, what I succeeded in doing is this:

  1. make a custom class to implement Calendar Store methods, and define that object as an AS variable:

set WBCCalStore to (call method "newWBCcalStore" of class "WBCcalStore")

  1. the methods of this custom class can be called with call methods, and certain things can be done with the returned values. for example:

set these_events to call method "getEventsinRangewithStart:withEnd:" of WBCCalStore with parameters {sd, ed}

  1. I can get events within a range (as example above)

What I can’t do is:

  1. DO ANYTHING with those events. Simply getting the title of these returned event objects I can’t yet do. I haven’t been successful in writing the “gettitle:” method referred to below:

set anEvent to item 1 of these_events
-- following does not work
--set anEventTitle to title of anEvent
-- following cannot make work so for now return notice string
set anEventTitle to call method "gettitle:" of WBCCalStore with parameter anEvent

(What I’d ultimately like to do is return a list or a record of all properties of the event that can of course be accessed in applescript)

  1. Be certain I’m dealing with memory and retaining and releasing objects properly.

  2. Figure out how to create and retain just one instance of the CalCalendarStore rather than creating a new instance for each method of this custom class.

  3. Know that I’m adding the Calendar Store framework to the project properly (as a reference to the /System/ version) without actually copying it into my builds

  4. Know that I can in fact add a framework that would be used on Leopard if available but that the project build would still launch on Pather/Leopard (and work as long as these methods aren’t called). In fact if that won’t be the case it is sort of a show stopper for me right there.

The sample project so far is here (alpha 0.1):

http://www.woodenbrain.com/sw/betas/WBCCalendarStore.zip

You seem to making progress. The problem seems to be with what is returned from the message call:

set these_events to call method "getEventsinRangewithStart:withEnd:" of WBCCalStore with parameters {sd, ed}

I inserted a routine to write the these_events list to desktop:

set a to (path to desktop as text) & "iCalEvents1.txt"
		set aa to open for access a with write permission
		write these_events to aa as list
		close access aa

and then opened up the list in SE, and this is what you get:

{item id 12, item id 13, item id 14, item id 15, item id 16, item id 17, item id 18, item id 19, item id 20, item id 21, item id 22}

The big problem now is trying to use data in that list inside of an iCal tell block is pretty much impossible, so it precludes using an AS routine to analyze these_events. It would seem that you are going to be stuck with working out everything in Obj-C for this. I am pretty short on free time this week, but this is truly interesting, so I will see what I can come up with playing around with your stuff.

I am pretty sure that the beta XCode is only functional on Intel machines, but I am not positive about that. I am often wrong, but seldom in doubt…

OK, I had a few minutes and got some good stuff. Here is the new method in WBCcalStore.m:

-(NSArray *)getEventsinRangewithStart:(NSDate *)theStartdate withEnd:(NSDate *)theEnddate {
	NSMutableArray *eventInformation = [NSMutableArray arrayWithCapacity:10];
	CalCalendarStore *calStore = [CalCalendarStore defaultCalendarStore];
  NSPredicate *predicate = [CalCalendarStore eventPredicateWithStartDate:theStartdate endDate:theEnddate calendars:[calStore calendars]];
	NSEnumerator *eventEnumerator = [[calStore eventsWithPredicate:predicate] objectEnumerator];
	id event;
	while (event = [eventEnumerator nextObject]) {
		[eventInformation addObject:[event title]];
	}
	return eventInformation;
}

And here is the revised code from the .applescript file:

if ce > 0 then
			set anEvent to item 1 of these_events
			my show_output("title of item 1 of " & ce & ": " & anEvent)
		end if

I think this is close to what you are looking for.

Thanks Craig, that’s promising. How would I modify that enumeration loop so that instead of returning a list of title strings, it returned a list of lists of all properties of the event, ie :

event class [CalEvent]:
isAllDay property
location property
recurrenceRule property
startDate property
endDate property
attendees property
isDetached property
occurrence property

superclass [calCalendarItem]:

Getting and Setting Properties
calendar property
notes property
url property
title property
uid property
dateStamp property
alarms property
Setting Alarms
“ hasAlarm
“ nextAlarmDate

of the above, I personally would need at least [calendar, notes, title, uid, startDate, endDate, location, and isAllDay]. But as long as we’re at it might as well make it general, right?

Then if I had that, the one other thing I would need is a way to pass a list of strings of names of calendars to the Obj-C method, so that this line:

NSPredicate *predicate = [CalCalendarStore eventPredicateWithStartDate:theStartdate endDate:theEnddate calendars:[calStore calendars]];

would fetch only from the appropriate calendars?

Then I think I’d be truly good to go.

Thanks for your time, I’ll continue to plug at it, too.

I am not sure about choosing the individual calendar (I will try to work on that), but you can re-configure the enumerator loop by adding an internal mutable array, and then adding that array to the final array:

while (event = [eventEnumerator nextObject]) {
		NSMutableArray *eachEvent = [NSMutableArray arrayWithCapacity:10];
		[eachEvent addObject:[event title]];
		[eachEvent addObject:[event calendar]];
		[eachEvent addObject:[event startDate]];
		[eachEvent addObject:[event endDate]];
		[eventInformation addObject:eachEvent];
	}

This particular routine returns a list like this:

{{"Dance Practice", item id 9, date "Tuesday, June 10, 2008 7:00:00 PM", date "Tuesday, June 10, 2008 8:30:00 PM"}, {"Piano Recital", item id 10, date "Friday, June 13, 2008 7:00:00 PM", date "Friday, June 13, 2008 9:00:00 PM"}, {"Piano Recital", item id 9, date "Friday, June 13, 2008 7:00:00 PM", date "Friday, June 13, 2008 9:00:00 PM"}, {"Dance Practice", item id 9, date "Saturday, June 14, 2008 8:00:00 AM", date "Saturday, June 14, 2008 10:00:00 AM"}, {"Work", item id 11, date "Saturday, June 14, 2008 9:00:00 AM", date "Saturday, June 14, 2008 12:00:00 PM"}, {"Kimbo/Derek Haynes BBQ", item id 11, date "Saturday, June 14, 2008 1:00:00 PM", date "Saturday, June 14, 2008 6:00:00 PM"}}

As you can see, the [eachEvent addObject:[event calendar]]; is not working right, but the dates are in there and accurate.

Thanks again… getting somewhere! but a long road to go :wink:

As for your last return in the list from “Calendar” that is understandable because it is returning a Calendar object, what we need is the name of the calendar object.

Similarly, if we were to able able to pass a list of calendar names to the handler, it would have to loop through them and create an array with the matching calendar objects. I don’t think I can pull that one off!

Here’s the rest of my report thus far:

These properties of the event class and the superclass can all be added:

[eachEvent addObject:[event uid]];
[eachEvent addObject:[event calendar]];
[eachEvent addObject:[event title]];
[eachEvent addObject:[event startDate]];
[eachEvent addObject:[event endDate]];
[eachEvent addObject:[event notes]];
[eachEvent addObject:[event occurrence]];
[eachEvent addObject:[event dateStamp]];

These 3 give yellow compile warnings:

[eachEvent addObject:[event isAllDay]];
[eachEvent addObject:[event isDetached]];
[eachEvent addObject:[event hasAlarm]];

warning: passing argument 1 of ‘addObject:’ makes pointer from integer without a cat
(note that all 3 of them are boolean)

This 1 gives red compile error:
[eachEvent addObject:[event location]];

error: incompatible type for argument 1 of ‘addObject:’
(this one makes no sense, should just be a string)

Then of the rest, several compile but cause handler to fail:

[eachEvent addObject:[event url]];
[eachEvent addObject:[event attendees]];
[eachEvent addObject:[event recurrenceRule]];
[eachEvent addObject:[event nextAlarmDate]];
[eachEvent addObject:[event alarms]];

Question:

When we define this:
NSMutableArray *eventInformation = [NSMutableArray arrayWithCapacity:10];

what if capacity is unknown?

Not a problem, since the array is mutable. You just need to plug in a number for the initialization of the object. You could use 1 if you want, I like 10.

I will see about the others over the next few days.

Fantastic. Nice team work.

I’ve rebuilt the demo project, this time with table output and placeholders for all the properties that don’t work in the method (numbered strings). So please work with this, same link as before:

http://www.woodenbrain.com/sw/betas/WBCCalendarStore.zip

First bummer to report is it crashes at launch at Tiger. This is even with checks for Leopard built in and no Cal Store code executed on Tiger. So it’s the added framework. So first thing needed is a way to programmatically load the framework. Again, I thought that the “#include” should have been sufficient? I haven’t made sense out of this thread yet but it may help:

http://lists.apple.com/archives/xcode-users/2007/Dec/msg00132.html

Second, in addition to what I reported above:

   // following works IN MOST CASES BUT SEEMS TO BREAK WITH RECURRING EVENTS, or possibly if NOTES IS BLANK
   //[eachEvent addObject:[event notes]];

So clearly there needs to be some error trapping for each (or most) properties added to the array.

Third, I do think there is some memory allocation / release issue, this from my system log:

Jun 11 01:01:04 G5-Dual Xcode[78679]: Xcode(78679,0xf0103000) malloc: free_garbage: garbage ptr = 0x30dc010, has non-zero refcount = 1
Jun 11 01:01:04 G5-Dual Xcode[78679]: Xcode(78679,0xf0103000) malloc: free_garbage: garbage ptr = 0x316a4d0, has non-zero refcount = 1
Jun 11 01:01:04 G5-Dual [0x0-0x3f13f1].com.apple.Xcode[78679]: Xcode(78679,0xf0103000) malloc: free_garbage: garbage ptr = 0x30dc010, has non-zero refcount = 1
Jun 11 01:01:04 G5-Dual [0x0-0x3f13f1].com.apple.Xcode[78679]: Xcode(78679,0xf0103000) malloc: free_garbage: garbage ptr = 0x316a4d0, has non-zero refcount = 1

This may also be due to Xcode itself though. (I have filed a bug report on Xcode 3.0 crashing on PPC with bugreporter before this.) Still it would seem prudent to do what is necessary.

Edit:

As of 7 AM EDT June 11, I rebuilt the project again, this time fixing a couple of issues with the table and sort of getting the calendar name. I say sort of because it works but there is a warning. Must have syntax off?


// following works, but we need calendar title instead
// [eachEvent addObject:[event calendar]];
// following works, but puts up warning during compile "NSCalendar may not respond to -title"
[eachEvent addObject:[[event calendar] title]];

It is available at the same URL.

It occurred to me also that:

  1. it will be necessary for my purposes and probably useful to others to have:
    A. use all calendars (current default)
    B. (EXCLUDE calendars whose names are) parameter
    C. (INCLUDE calendars whose names are) parameter.

  2. Perhaps the do/while loop to make the NSArray of event properties should be a subroutine, so that other methods can be added later and the same ordered event list could be returned.

Thanks!

At 10:30 EDT June 11, I uploaded another revision to the project with same URL.

This time it has a second table for calendars with their properties, and a check box on the left. The idea is to implement passing a list of calendar uid’s to the handler, and then in the handler using one of the methods, calendarWithUID:, to create an array of calendars we actually want to include. Possibly if an empty list is passed then use the default “calendars”

As with the events properties, there are 2 that don’t seem to work: notes (same reason?) and isEditable (same reason, boolean)…

At 18:25 EDT June 11, I uploaded another revision to the project with same URL.

This is about as far as I can take it for now without some more Obj-C help.

Added:

– search uncompleted todos that are due before given end date [obj-c & applescript]
– get todos and their properties, mapping them as closely as possible to the events’ properties [obj-c & applescript]
– output todos on same table as events
– AS routine for checking which of the calendars’ check boxes on the tables are enabled and reporting and producing a list of UIDs to pass to the handler.

(* now to search, you must get calendars first, though it actually has no effect yet)

Modified
– obj-c methods to include receivng list of calendar uids.

Added, but not successfully:
– obj-c conditional statement and build array loop to try to deal with passed list of uids.
– obj-c subroutine to get make the eachEvent array, for less code clutter.

Notes:

To-Dos, as with tasks, seem to have certain properties that cause the handler to fail. It seems that booleans are tricky, and anything with null or missing value causes errors.

So…
priorities for completion of this demo project as I see it are:

  1. load Calendar Store framework programmatically so it can run on Tiger.
  2. correctly implement passing a list of calendar uids to the fetch events & fetech todos handlers.
  3. include necessary coercion and error checking for each property for events and tasks
  4. address any possible memory release issues

EDIT:

with regards to priority #3, I figured out the boolean problem, by specifying like this:
[eachEvent addObject:[NSNumber numberWithBool:[event isAllDay]]]; //#9

as for the others, they all relate to nil values. have not figured out yet how to fix those.

debugger console shows:
[NSCFArray insertObject:atIndex:]: attempt to insert nil

if, following above pattern, I try this:
[eachEvent addObject:[NSString stringWithFormat:[event notes]]]; //#6

then debugger console shows:
-[NSPlaceholderString initWithFormat:locale:arguments:]: nil argument

so how do we force a nil or 0 or missing value or anything in there to the NSMutableArray?

(have not updated the project online yet)

OK, MAJOR update here… I checked all “nil” values with conditional statements and return “” for strings and 0 for dates. certainly not the most efficient way, but it works.

So now the only properties that DON’T work are:

  1. event location. no idea what’s wrong there (would like to fix that one)
  2. some arrays of other objects (such as alarms, recurrenceRules, attendees) [can live without these for now]

Now, “priorities for completion” are:

  1. load Calendar Store framework programmatically so it can run on Tiger.
  2. correctly implement passing a list of calendar uids to the fetch events & fetech todos handlers.
  3. address any possible memory release issues

Please help with these, thanks!

New version uploaded with same URL.

You are just chugging along!! Sorry that I have been out of touch. I have been keeping up with your posts, but have been short on coding time this week. Tonight looks OK, though, we shall see.