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 Sponsored I want a Press KitiOS 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.
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.
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