EventKit
Use EventKit for calendar and reminder authorization, CRUD, recurrence, alarms, and system editors.
Contents
- Availability
- Setup
- Authorization
- Creating Events
- Fetching Events
- Reminders
- Recurrence Rules
- Alarms
- EventKitUI Controllers
- Observing Changes
- Common Mistakes
- Review Checklist
- References
Availability
- iOS 17+: Use granular full/write-only request methods; legacy
requestAccess(to:)no longer prompts and throws. The system event editor can create an event without app calendar access. For iOS 10–16, guard those APIs, use the legacy request plusNSCalendarsUsageDescription/NSRemindersUsageDescription; EventKitUI may also needNSContactsUsageDescription. - iOS 26+: The typed
EKEventStore.EventStoreChanged/.changedmessage is available behind a guard. KeepEKEventStoreChangedfor earlier systems.
Setup
Info.plist Keys
Add the usage description for the access path selected in Authorization. Do not request broader access merely to simplify the setup path.
The authorization-free system editor path needs no calendar usage string. Direct writes need write-only or full access; reads need full access. Reminders have only full access.
Event Store
Create a single EKEventStore instance and reuse it. Do not mix objects from
different event stores.
Authorization
Request the narrowest access that matches the feature. Apply the versioned request path in Availability.
Full Access to Events
Call try await eventStore.requestFullAccessToEvents() when the app needs to
read, edit, delete, or fetch calendar events.
Write-Only Access to Events
Use when your app only creates events (e.g., saving a booking) and does not need to read existing events.
Call try await eventStore.requestWriteOnlyAccessToEvents() before direct
EventKit writes that do not use EKEventEditViewController.
Write-only access can create events but cannot fetch calendars or events, including app-created events. Use full access for later query, verification, modification, or sync.
Full Access to Reminders
Call try await eventStore.requestFullAccessToReminders() before reading,
creating, editing, or deleting reminders.
Checking Authorization Status
Use EKEventStore.authorizationStatus(for: .event) or .reminder before work.
Handle .notDetermined, .fullAccess, .writeOnly, .restricted, .denied,
and @unknown default; only .fullAccess supports event/reminder reads.
Creating Events
Setting a Specific Calendar
Adding Structured Location
Fetching Events
After the full-access gate in Authorization, use a date-range
predicate to query events. The events(matching:) method returns occurrences of
recurring events expanded within the range. Event predicates are capped to a
four-year span, and events(matching:) /
enumerateEvents(matching:using:) are synchronous and return only committed
events.
Fetching a Single Event by Identifier
Reminders
Creating a Reminder
Fetching Reminders
Reminder fetches are asynchronous and return through a completion handler.
Completing a Reminder
Recurrence Rules
Use EKRecurrenceRule to create repeating events or reminders.
Simple Recurrence
Complex Recurrence
Editing Recurring Events
When saving changes to a recurring event, specify the span:
Alarms
Attach alarms to events or reminders to trigger notifications.
For reminder geofences, put an EKStructuredLocation and .enter / .leave
proximity on an EKAlarm, then add it to the reminder. See
references/eventkit-patterns.md [blocked] for the full
location-based reminder pattern.
EventKitUI Controllers
EKEventEditViewController — Create/Edit Events
Present the system event editor for creating or editing events.
On the authorization-free path in Availability, the editor runs out of process with its own calendar access. Do not inspect the dismissed controller to learn what was saved; refetch only with separate full access.
EKEventViewController — View an Event
EKCalendarChooser — Select Calendars
EKCalendarChooser requires write-only or full calendar access. In write-only
apps, the chooser behaves as writable-calendars-only and only allows a single
writable calendar selection.
Observing Changes
Register for EKEventStoreChanged notifications to keep your UI in sync when
events are modified outside your app (e.g., by the Calendar app or a sync).
Always re-fetch events after receiving this notification. Previously fetched
EKEvent, EKReminder, and EKCalendar objects may be stale. The notification
is posted on the main actor.
Common Mistakes
DON'T: Use legacy requestAccess(to:) on current systems
Keep it only in the compatibility fallback from Availability.
DON'T: Save events to a read-only calendar
DON'T: Ignore timezone when creating events
DON'T: Forget to commit batched saves
DON'T: Mix EKObjects from different event stores
Review Checklist
- Correct
Info.plistusage description keys added for calendars and/or reminders - Authorization follows the version split in Availability
- Write-only calendar access used only for direct event creation, not event/calendar reads
- Authorization status checked before fetching or saving
- Full access required before any event or reminder fetch
- Single
EKEventStoreinstance reused across the app - Events saved to a writable calendar (
allowsContentModificationschecked) - Recurring event saves specify correct
EKSpan(.thisEventvs.futureEvents) - Batched saves validate writable calendars, stage with
commit: false, call throwingcommit(), and on failurereset()unsaved state, discard every invalidatedEKObject, then refetch or reconstruct before retry -
EKEventStoreChangednotification observed to refresh stale data - Change observation uses the classic notification or guarded typed message per Availability
- Timezone set explicitly for location-specific events
- EKObjects not shared across different event store instances
- EventKitUI delegates dismiss controllers in completion callbacks
References
- Extended patterns (SwiftUI wrappers, predicate queries, batch operations): references/eventkit-patterns.md [blocked]
- EventKit framework
- EKEventStore
- EKEvent
- EKReminder
- EKRecurrenceRule
- EKCalendar
- EventKit UI
- EKEventEditViewController
- EKCalendarChooser
- Accessing the event store
- Creating a recurring event


