How to use EKCalendarChooser in Swift to let user select calendar in iOS

Learn about working with EventKitUI to let user select their calendars with the calendar picker. Permissions and delegate explained.

Published: May 22, 2020
I want a Press Kit

iOS offers great way to work with calendar events and one part is EKCalendarChooser from the Event Kit framework. As the name suggests, this lets user select calendars from those on device. It is basically the calendar picker.

This controller takes care of displaying all the user’s calendars and even offers pull to refresh to refresh them should the user choose to do so. In this short guide I am going to show you how to use the EKCalendarChooser and what it offers.

Selecting calendars with EKCalendarChooser

Configuring Info.plist

The first step is to modify Info.plist with usage descriptions for calendar and contacts. You need both because user may have shared calendars which show people's names.

Those keys are NSCalendarsUsageDescription and NSContactsUsageDescription. Add them to the Info.plist in your app and describe why do you need this access.

Now let’s move to the code.

Import and preparation

Start by importing EventKitUI:

import EventKitUI

The controller needs an instance of EKEventStore to work with events, so let's create one at the class level:

let eventStore = EKEventStore()

We first start with method preparations and then tie them together in a method that will react to button tap in our example.

Presenting the calendar chooser

Let’s start with the main stuff and that is creating EKCalendarChooser and presenting it:

func showCalendarChooser() {
    let vc = EKCalendarChooser(selectionStyle: .single, displayStyle: .allCalendars, entityType: .event, eventStore: eventStore)
    // customization
    vc.showsDoneButton = true
    vc.showsCancelButton = true

    // dont forget the delegate
    vc.delegate = self

    let nvc = UINavigationController(rootViewController: vc)

    self.present(nvc, animated: true, completion: nil)
}

Couple things to note. We are wrapping it in the UINavigationController so it has nice navigation bar with optional buttons.

The selectionStyle parameter is used to specify whether we want to select single calendar or multiple. The values are .single and .multiple respectively.

With .displayStyle we can either choose all calendars or only the writable ones.

Delegate

This code won’t compile just yet. Add EKCalendarChooserDelegate delegate conformace to the view controller where you plan to let user select calendars.

extension ViewController: EKCalendarChooserDelegate {
}

If you were to try to show the EKCalendarChooser right now, you would get nice explanation with lock icon that says that your app does not have permissions to see the calendars. We need to ask first.

Issue with permissions when selecting calendars

Requesting calendar access

Let’s add method to request access:

func requestAccess() {
    eventStore.requestAccess(to: .event) { (granted, error) in
        if granted {
            // may not be called on the main thread..
            DispatchQueue.main.async {
                self.showCalendarChooser()
            }
        }
    }
} 

The completion block may not be called on the UI (main) thread, so we are using DispatchQueue.main.async to fix that.

We are almost done.

@IBAction func chooseCalendarTapped(_ sender: UIButton) {
    let authStatus = EKEventStore.authorizationStatus(for: .event)

    switch authStatus {
    case .authorized:
        showCalendarChooser()
    case .notDetermined:
        requestAccess()
    case .denied:
        // Explain to the user that they did not give permission
        break
    case .restricted:
        break
    @unknown default:
        preconditionFailure("Who knows what the future holds 🤔")
    }
}

We first ask for the current authorization status and based on the result either show the calendar chooser, ask for permissions or do nothing in this example case. The .restricted case can happen due to parental controls for example.

Implementing delegate methods

Now let’s turn to the EKCalendarChooserDelegate and implement the methods.

func calendarChooserDidFinish(_ calendarChooser: EKCalendarChooser) {
        print(calendarChooser.selectedCalendars)
        dismiss(animated: true, completion: nil)
}

func calendarChooserSelectionDidChange(_ calendarChooser: EKCalendarChooser) {
        print("Changed selection")
}

func calendarChooserDidCancel(_ calendarChooser: EKCalendarChooser) {
        print("Cancel tapped")
        dismiss(animated: true, completion: nil)
}

Since we are presenting the controller modally, we have to dismiss it. You can access the selectedCalendars property to get instance of EKCalendar that the user selected.

Full example

The full example is available on GitHub. Thanks for reading!

Uses: Xcode 12 & Swift 5.3

Filip Němeček profile photo

WRITTEN BY

Filip Němeček @nemecek_f@iosdev.space

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀