Creating Calendars with Event Kit and Swift

Updated on April 19, 2016 – Swift 2.1 | Added example project

Apple’s Event Kit framework empowers developers to interact with an iOS device’s calendar database. Not only can we read calendars and events from the database, we can also create calendars.

In a previous article, we looked at how to handle asking the user for permission to access their calendars. Now my goal is to show you how to create local calendars on the user’s device programmatically with Swift using the Event Kit framework.

Here’s a demo of what we’re going for by the time I’m finished with this article:
Create Calendar Demo

If you’d like to tinker with the code for yourself, you can download the example project:

Import EventKit

Step 1 in this whole process will be to import EventKit at the top of your Swift file:

1import UIKit
2import EventKit
3
4// ...

Importing EventKit gives us access to everything we need to work with calendars.

General outline for creating a local calendar

Now, we’ll proceed with creating a local calendar on the user’s device. It’s important to note that there are other kinds of calendars that you can create. For example, you can create calendars that sync with iCloud. For now though, we’re going to narrow the focus down to just creating the calendar on the user’s local device.

Here’s the general outline (and then some code):

  • Create an EKEventStore instance
  • Create a new EKCalendar instance using that event store instance
  • Configure the new calendar’s title
  • Wire up the new calendar’s source
    • Obtain a list of the available sources from the event store instance
    • Filter that list down to the EKSourceTypeLocal source type
    • Assign it to the calendar’s source property
  • Save the calendar using the event store instance
  • Handle any problems that might have occurred

Code example

That’s the general outline… Now for the code!

 1// Create an Event Store instance
 2let eventStore = EKEventStore();
 3
 4// Use Event Store to create a new calendar instance
 5// Configure its title
 6let newCalendar = EKCalendar(forEntityType: .Event, eventStore: eventStore)
 7
 8// Probably want to prevent someone from saving a calendar
 9// if they don't type in a name...
10newCalendar.title = "Some Calendar Name"
11
12// Access list of available sources from the Event Store
13let sourcesInEventStore = eventStore.sources 
14
15// Filter the available sources and select the "Local" source to assign to the new calendar's
16// source property
17newCalendar.source = sourcesInEventStore.filter{
18    (source: EKSource) -> Bool in
19    source.sourceType.rawValue == EKSourceType.Local.rawValue
20}.first!
21
22// Save the calendar using the Event Store instance
23do {
24    try eventStore.saveCalendar(newCalendar, commit: true)
25    NSUserDefaults.standardUserDefaults().setObject(newCalendar.calendarIdentifier, forKey: "EventTrackerPrimaryCalendar")
26} catch {
27    let alert = UIAlertController(title: "Calendar could not save", message: (error as NSError).localizedDescription, preferredStyle: .Alert)
28    let OKAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
29    alert.addAction(OKAction)
30    
31    self.presentViewController(alert, animated: true, completion: nil)
32}

The most confusing part of the code above for me was obtaining the right source to assign to the new calendar’s source property. Let’s unpack that for a second…

Obtaining and assigning the calendar’s source

The eventStore instance gives us the ability to query for a listing of its relevant calendar source types. But why are we going to the event store just to get a list of all the sources so that we can filter it down to just the one we want? Well, because this is the only way to get EKSource instances! Take a look at this quote from the Apple Documentation:

You do not create instances of EKSource. You retrieve EKSource objects from an EKEventStore object. Use the sources property to get all the EKSource objects for an event store, and use the methods in this class to access properties of the source object. (emphasis added)

So that answers the question of why we query the event store for that list of source types. But now, how do we narrow that list down to the one we want? That’s where the call to filter comes in…

First let’s isolate that code segment from the rest so it’s clear what we’re analyzing:

1newCalendar.source = sourcesInEventStore.filter{
2    (source: EKSource) -> Bool in
3    source.sourceType.rawValue == EKSourceType.Local.rawValue
4}.first!

The goal of this code is to take the list of sources in the event store, and filter them so that only the one matching the value of EKSourceTypeLocal is returned. This is easily accomplished using the filter function on the array of EKSources that’s returned by the event store.

But filter also returns an array, so to get the single source we’re looking for, we’ll simply grab the first element out of the array filter returns, and assign it to the new calendar’s source property. There are no duplicated EKSourceTypes in the list returned by the event store, so our filter expression should only return one match wrapped in an array.

That’s it for configuring the calendar. The remainder of the code uses the event store instance to save the calendar, and handle any errors that might occur with the save process.

Saving the calendar identifier

One last thing to note is that if you’re creating a calendar for your app to store events in, you probably want to stash the calendar’s identifier value somewhere, so that you can query the event store for the calendar directly, at a later point in time.

Using NSUserDefaults.standardUserDefaults() is a convenient way to store this calendar identifier value. The code to pay attention to is highlighted in this snippet:

 1// ...
 2
 3// Save the calendar using the Event Store instance
 4do {
 5    try eventStore.saveCalendar(newCalendar, commit: true)
 6    NSUserDefaults.standardUserDefaults().setObject(newCalendar.calendarIdentifier, forKey: "EventTrackerPrimaryCalendar")
 7} catch {
 8    let alert = UIAlertController(title: "Calendar could not save", message: (error as NSError).localizedDescription, preferredStyle: .Alert)
 9    let OKAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
10    alert.addAction(OKAction)
11    
12    self.presentViewController(alert, animated: true, completion: nil)
13}

So assuming that the calendar was saved successfully without error, we’ll simply access the standard user defaults, and insert a new object (our calendar’s identifier) for a key that we’ll use to retrieve the identifier again later.

Wrapping up

Having the ability to create a calendar for an iOS application using Event Kit is a powerful thing if you’re wanting to take advantage of some of the built-in event-related features of the iOS platform. In this article we saw how to create a calendar using Event Kit and Swift. Additionally we analyzed some of the less-than-intuitive pieces of accessing the event store for a list of sources. We concluded by saving the new calendar’s identifier to NSUserDefaults so that we could easily retrieve the calendar from the event store at a later time.

comments powered by Disqus