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

nemecek_f

Filip Němeček

Posted on March 20, 2020

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

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 calendar.

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.

EKCalendarChooser in action

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.

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.

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.

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.

System screen informing us and the user that the app does not have persmission to access calendars

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.

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.

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

Scan it - wide banner

💖 💪 🙅 🚩
nemecek_f
Filip Němeček

Posted on March 20, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related