Manage events in iOS calendar with EventKit iOS 16.01.2022

The EventKit framework consists of a range of classes designed specifically to provide access to the calendar database and to facilitate the management of events (EKEventStore), reminders (EKReminder) and alarms (EKAlarm).

  • EKEventStore class provides an interface between applications and the underlying calendar database. The calendar database can contain multiple calendars. Each calendar in a database is represented in code in the form of an EKCalendar object. An EKEventStore object must request access to the calendar at the point that it is initialized. This request must specify whether access is required for calendar events (.event) or reminders (.reminder).
  • Within each calendar there are events and reminders, each of which is managed in code using the EKEvent and EKReminder classes respectively.
  • EKAlarm class is used to configure alarms to alert the user at a specified point in the future.

Since this component interacts with system, the first step is to add two permission into Info.plist for your project.

  • NSCalendarsUsageDescription like "Allow access to calendar in order to save events".
  • NSContactsUsageDescription. You need this permission because user may have shared calendars which show people's names.

Second, there are two approach to add a new event either via EKEventEditViewController or just direct to EKEventStore.

Adding events to the a calendar with a modal

EventKit provides us with an EKEventEditViewController which can be presented modally with an event’s pre-filled information so the user can edit some fields of the event before adding it to a calendar.

To add simple event via EKEventEditViewController you basically need just an instance of EKEvent and EKEventStore.

The EKEvent is the event itself, it has properties like title, startDate etc, those will be shown in the controller and subsequently saved to the user's calendar.

Import EventKitUI like so

import EventKitUI

Start by requesting access

let eventStore = EKEventStore()

switch EKEventStore.authorizationStatus(for: .event) {
    case .denied, .restricted:
        print("Access denied")
    case .notDetermined:            
        eventStore.requestAccess(to: .event) { [weak self] (granted, error) in
            DispatchQueue.main.async {
                if granted {
                    self!.insertEvent(store: eventStore)
                } else {
                    print("Access denied")
                }

            }           
        }
    case .authorized:
        insertEvent(store: eventStore)
    default:
        break
}

Create the controller object

let eventVC = EKEventEditViewController()
eventVC.editViewDelegate = self // don't forget the delegate
eventVC.eventStore = EKEventStore()

Next create your EKEvent instance and use the EKEventStore instance from eventVC.

let event = EKEvent(eventStore: eventVC.eventStore)
event.title = "My first event!"
event.startDate = Date()
eventVC.event = event

And show it

present(eventVC, animated: true)

You need the delegate method to close the view controller

func eventEditViewController(_ controller: EKEventEditViewController, 
        didCompleteWith action: EKEventEditViewAction) {
    dismiss(animated: true, completion: nil)
}

Adding events to the user’s default calendar programmatically

Following is a snippet for inserting a event to default calendar

func insertEvent(store: EKEventStore) {
    // get list of all calendaers
    // let calendars = store.calendars(for: .event)

    let today = Date()

    let newEvent = EKEvent(eventStore: eventStore)
    newEvent.calendar = eventStore.defaultCalendarForNewEvents
    newEvent.title = "My first event!"
    newEvent.startDate = today
    newEvent.endDate = Calendar.current.date(byAdding: .day, value: 1, to: today)

    // set alarm 5 minutes before event
    let alarm = EKAlarm(relativeOffset: TimeInterval(-60 * 5))
    newEvent.addAlarm(alarm)

    do {
        try store.save(event, span: .thisEvent)
    } catch {
       print("Error saving event in calendar")             }
    }
}

Also we can check if a event already exists in the calendar

func checkEventExists(store: EKEventStore, event eventToAdd: EKEvent) -> Bool {
    let predicate = store.predicateForEvents(withStart: eventToAdd.startDate, end: eventToAdd.endDate, calendars: nil)
    let existingEvents = eventStore.events(matching: predicate)

    let exists = existingEvents.contains { (event) -> Bool in
        return eventToAdd.title == event.title && event.startDate == eventToAdd.startDate && event.endDate == eventToAdd.endDate
    }
    return exists
}