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:
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 anEKEventStore
object. Use the sources property to get all theEKSource
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.