HI–
I have just succeeded in selecting a years’ worth of events – including recurring events – from an iCal-maintained calendar (without actually using the iCal app) and I thought I’d share the code here in case it might help someone else.
I keep about a dozen calendars in the system calendar data base maintained by my iCal application. The code below goes directly to the system calendar data base, selects one calendar out of the dozen, then makes a list of all the events on that calendar for the next year.
I had tried to do this using ‘tell application “iCal”’ AppleScript commands, but recurring events would not come out properly. I tried following Nigel Garvey’s technique to parse the recurrence string, but I never got it working with confidence.
Then I found out that Cocoa has the exact interface I was looking for: (http://developer.apple.com/library/mac/#documentation/AppleApplications/Conceptual/CalendarStoreProgGuide). As recurrence is so complicated, I imagine there might be some recurrence strings that even Apple’s code cannot parse correctly, but up with that restriction I am for the present willing to put.
All (!) I had to do was learn to use XCode (http://developer.apple.com/technologies/tools/xcode.html), study the notation for AppleScriptObjC, (http://developer.apple.com/library/mac/#releasenotes/ScriptingAutomation/RN-AppleScriptObjC/index.html), learn to read Cocoa documentation (http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/Introduction/Introduction.html) and absorb a lot of wisdom from the good people on this board.
After I initiated my XCode project, I had to add the CalendarStore.framework to “Linked Frameworks” and " #import <CalendarStore/CalendarStore.h>" to “Prefix.pch”. Then I used Help → Developer Documentation (option-command-?) to look up each Cocoa class involved and Run → Console to see my ‘log’ statements come out. It was tedious, but fun.
One extra bonus of this technique is that the events appear (so far) to come out in “event start date” order. Using iCal, they didn’t.
Here’s the code:
I left in all my log commands so that you could see the intermediate steps as I did.
on applicationWillFinishLaunching_(aNotification)
-- Insert code here to initialize your application before any files are opened
log "applicationWillFinishLaunching_"
my MakeMCCVhtml()
quit
end applicationWillFinishLaunching_
on MakeMCCVhtml()
tell class "NSDate" of current application
set TimeNow to |date|()
set YearFromNow to ¬
dateWithTimeIntervalSinceNow_(60 * 60 * 24 * 365)
end tell
log "TimeNow=" & (description of TimeNow)
log "YearFromNow=" & (description of YearFromNow)
tell class "CalCalendarStore" of current application to ¬
set SharedCalendarStore to defaultCalendarStore()
tell SharedCalendarStore to ¬
set MyCalendars to calendars()
repeat with aCalendar in MyCalendars
if ((type of aCalendar as text) is "CalDAV" and ¬
(|title| of aCalendar as text) is "MCCV Congregational Events") then
set CECalendar to aCalendar
exit repeat
end if
end repeat
tell class "NSArray" of current application to ¬
set CECalendarInArray to arrayWithObject_(CECalendar)
tell class "CalCalendarStore" of current application to ¬
set NextYearPredicate to ¬
eventPredicateWithStartDate_endDate_calendars_(TimeNow, YearFromNow, CECalendarInArray)
tell SharedCalendarStore to ¬
set NextYearsEvents to eventsWithPredicate_(NextYearPredicate)
log "class of SharedCalendarStore=" & ¬
|description|() of (|class|() of SharedCalendarStore)
log "class of MyCalendars=" & ¬
|description|() of (|class|() of MyCalendars)
log "class of NextYearPredicate=" & ¬
|description|() of (|class|() of NextYearPredicate)
log "class of NextYearsEvents=" & ¬
|description|() of (|class|() of NextYearsEvents)
log "count of NextYearsEvents=" & ¬
(count of NextYearsEvents)
set anEvent to item 1 of NextYearsEvents
log "class of anEvent=" & ¬
|description|() of (|class|() of anEvent)
log "description of anEvent=" & ¬
|description|() of anEvent
log "dateStamp of anEvent=" & ¬
|description|() of (|dateStamp|() of anEvent)
log "occurrence of anEvent=" & ¬
|description|() of (occurrence() of anEvent)
log "startDate of anEvent=" & ¬
|description|() of (|startDate|() of anEvent)
log "endDate of anEvent=" & ¬
|description|() of (|endDate|() of anEvent)
log "title of anEvent=" & ¬
|title|() of anEvent
log "notes of anEvent=" & ¬
notes() of anEvent
log "isAllDay of anEvent=" & ¬
(|isAllDay|() of anEvent)
log "recurrenceRule of anEvent=" & ¬
(|recurrenceRule|() of anEvent)
set PreviousStartTime to (description of (|startDate|() of anEvent) as text)
repeat with anEvent in NextYearsEvents
set EVStartTime to (description of (|startDate|() of anEvent) as text)
if EVStartTime comes before PreviousStartTime then
set OrderFlag to "***"
else
set OrderFlag to " "
end if
set PreviousStartTime to EVStartTime
log OrderFlag & " " & EVStartTime & ": " & ¬
(|title|() of anEvent)
if (|recurrenceRule|() of anEvent) is missing value then
else
log "recurrenceRule=" & ¬
(description of (|recurrenceRule|() of anEvent))
end if
end repeat
log "Done."
return
end MakeMCCVhtml
“Build and Run” produced the following Console output:
I’d welcome your comments.
–Gil