I’m old school when it comes to certain things. When the Titanium Powerbook G4 came out, I was excited because I could have six Terminal windows open at once. I still use Emacs. My biggest complaint about Xcode 4 is they got rid of the Emacs client integration.
On the other hand, sometimes newer things work for me as well. When Reminders came out in iOS 5, I started using it, but wasn’t diligent about it. Once it got iCloud and Mountain Lion integration, I started using it a lot more. I even use Siri to make a reminder. I make a reminder on my iPhone, and I have it on all my devices, so I actually keep track of things.
Recently, I started using the command line password manager, pass, to manage my passwords. I tried other password managers, and this is a case where the simpler command-line interface works for me. You type
pass and get a list of all your accounts and passwords, which you catagorize. There are simple commands to add, extract and delete passwords. It’s all encrypted using GPG, so I don’t need to worry if I lose my laptop. All the data is stored in my home directory, so it’s part of my Time Machine backups.
1 2 3 4 5 6 7 8 9 10 11 12
I wanted some similar for to access reminders. Now Apple’s been pretty good about making command-line equivalents for a lot of UI tools (think DiskUtility.app and diskutil), but this is one where they don’t have anything. A quick Google didn’t really find anything for me either.
So I have roll up my sleeves and write something myself.
Turns out that along with releasing Reminders in Mountain Lion, Apple has given us an SDK to access the data store behind Calendar and Reminders: EventKit. EventKit was first introduced in the OSX and iOS SDKs to give access to Calendar. Now with OSX Mountain Lion and iOS 6, Apple extended EventKit to grant access to Reminders. This extension to EventKit was possible because underneath it all, items in Reminders (“reminders”) are an extension of events in Calendar (“events”). In fact, both sets of data are stored in a single database, which Apple calls the Calendar database.
Why not use something like SQLite and let us access the calendar database directly? Odds are that your local calendar(s) are synchronized with calendars on server (i.e. iCloud, gCal, etc), typically using CalDAV. In order to maintain that synchronization, it’s easier to mediate all access to the local Calendar database to know when changes need to propagated to the server and known when changes have arrived from the server.
Note: I’m building an application to run in OSX. Most of what I cover is applicable for iOS, but there are some differences. For example, instantiating the event store is different between OSX and iOS. Read the Calendar and Reminders Programming Guide. Also, I’m building an application to access reminder, I won’t be covering event access.
Let’s review the EventKit framework with respect to reminders.
At the root of EventKit is the class
EKEventStore. This represents the Calendar database.
EKEventStore is a fairly heavy-weight object, taking a (relatively) long time to instantiate and release. As a result, your application should only instantiate a single
To instantiate your event store, you invoke the initializer,
EKEntityMask for reminders.
With iOS, you instantiate your event store with a simple call to
init. Then, you request access to reminders using
requestAccessToEntityType:completion:. This invoking this method will cause iOS to ask the user if your application is allowed to access your Calendar database. You need to handle both cases in the completion block. This code is not in the OSX EventKit, since access to the Calendar database is granted automatically.
1 2 3 4 5
EventKit defines the class
EKCalendar to represent a calendar. Events are represented with the class
EKEvent. Reminders are modeled with the
EKReminder are extensions of the abstract class
EKCalendarItem. Rather than defining a new class to contain a list of reminders, Apple chose to leverage the
EKCalendar class as the container of reminders. Conceptually, Reminders calls these Reminder Lists, but internal to the event store, they are just instances of
EKEventStore defines two properties to access default the calendar and reminder list.
If you want to retrieve all calendars or reminder lists in the event store, you use the
calendarsForEntityType: method, specifying the
EKEntityType you want.
There’s another method,
calendarWithIdentifier:, that fetches a specific calendar, assuming you know the calendar’s unique identifier.
To create a new reminder, use the
EKReminder class method
reminderWithEventStore:. In order for the reminder to be valid, you need to provide values for the
1 2 3
To specify a start date, you use the
startDateComponents property. To specify a due date, use the
dueDateComponents property. These two properties are of the
NSDateComponents(doc) class, not the
There are two EKReminder properties associated with reminder completion:
NSDateinstance marking when the reminder was completed.
Setting on of these properties will adjust the other. For example, if you set
completionDate will be set to the current date. Setting
nil. Conversely, setting
completionDate to a date will set
YES; and setting
nil will set
To save a reminder, call the
saveReminder:commit:error: method. To delete a reminder, use the
1 2 3 4 5
1 2 3 4 5
In both cases, the
commit flag is used to tell the event store whether to apply the save/delete immediately, or queue the action as part of a batch. If you pass
commit:NO, then the changes will not happen until you invoke the EKEventStore method,
1 2 3 4 5
To retrieve your reminders from the event store, you use the
fetchRemindersMatchingPredicate:completion: method. Predicates are objects that encapsulate the conditions to perform a search or query. Even though we use an
NSPredicate, we can’t construct our EventKit predicate by hand. We have to use one of the three predicate construction methods on
predicateForIncompleteRemindersWithDueDateStarting:ending:calendars:find all incomplete Reminder items within a date range, for a given array of Reminder Lists.
predicateForCompletedRemindersWithCompletionDateStarting:ending:calendars:find all completed Reminder items within a date rage for a given array of Reminder Lists.
predicateForRemindersInCalendars:find all Reminder items for a given array of Reminder Lists.
To fetch across all reminder lists (calendars), you can simply specify
nil for the calendar array.
For the predicate generator methods that take date ranges, you can use the
NSDate class methods
distantFuture to fetch all reminder items.
Once you have your predicate, you can perform your fetch, with a completion handler block.
1 2 3 4 5
The completion handler block is send an array of reminder items that match the predicate. This fetch is performed asynchronously and doesn’t need to be dispatched to another thread. However, the completion handler block is invoked on the main thread, which caused problems for me (I’ll get to that in a little bit).
As with calendars, there is a method to retrieve a specific reminder if you know its unique identifier,
calendarItemWithIdentifier:. You probably won’t use this method as it’s unlikely that you’ll have an item’s unique identifier handy.
Alarms, Recurrence, and External Changes
I’m not going to cover these features in any detail, since I don’t need them for my command-line application. I’ve provided some links to the Apple documentation.
- Alarms: You can set an alarm for a given reminder, which can be time or location based.
- Recurrence: You can set a reminder to repeat.
- External Changes: As I mentioned earlier, odds are your calendars are tied to a server somewhere. As a result changes from the outside need to be reflected in you EventKit application.
Coming Up: Building Our Command-line Reminders Tool
Ok, that’s a pretty good review of the EventKit framework. My next post will use this information to build the command-line tool.