dpearson2699/swift-ios-skills3 files

Eventkit

Create, read, and manage calendar events and reminders using EventKit and EventKitUI. Use when adding events to the user's calendar, creating reminders, setting recurrence rules, requesting calendar or reminders access, presenting event editors, choosing calendars, handling alarms, observing calendar changes, or working with EKEventStore, EKEvent, EKReminder, EKCalendar, EKRecurrenceRule, EKEventEditViewController, EKCalendarChooser, or EventKitUI views.

Specification
Skill ID
dpearson2699/swift-ios-skills/eventkit
Publisher
dpearson2699
Repository
swift-ios-skills
Installs
0
Files
3
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

dpearson2699/swift-ios-skills/eventkitInstalls these files
  • SKILL.md
  • evals/evals.json
  • references/eventkit-patterns.md

What this skill tells the agent

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 plus NSCalendarsUsageDescription / NSRemindersUsageDescription; EventKitUI may also need NSContactsUsageDescription.
  • iOS 26+: The typed EKEventStore.EventStoreChanged / .changed message is available behind a guard. Keep EKEventStoreChanged for 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.

import EventKit

let eventStore = EKEventStore()

Authorization

Request the narrowest access that matches the feature. Apply the versioned request path in Availability.

KeyAccess Level
NSCalendarsFullAccessUsageDescriptionRead + write events
NSCalendarsWriteOnlyAccessUsageDescriptionDirect write-only event creation
NSRemindersFullAccessUsageDescriptionRead + write reminders

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

func createEvent(
    title: String,
    startDate: Date,
    endDate: Date,
    calendar: EKCalendar? = nil
) throws {
    let event = EKEvent(eventStore: eventStore)
    event.title = title
    event.startDate = startDate
    event.endDate = endDate
    event.calendar = calendar ?? eventStore.defaultCalendarForNewEvents

    try eventStore.save(event, span: .thisEvent)
}

Setting a Specific Calendar

// List writable calendars
let calendars = eventStore.calendars(for: .event)
    .filter { $0.allowsContentModifications }

// Use the first writable calendar, or the default
let targetCalendar = calendars.first ?? eventStore.defaultCalendarForNewEvents
event.calendar = targetCalendar

Adding Structured Location

import CoreLocation

let location = EKStructuredLocation(title: "Apple Park")
location.geoLocation = CLLocation(latitude: 37.3349, longitude: -122.0090)
event.structuredLocation = 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.

func fetchEvents(from start: Date, to end: Date) -> [EKEvent] {
    let predicate = eventStore.predicateForEvents(
        withStart: start,
        end: end,
        calendars: nil  // nil = all calendars
    )
    return eventStore.events(matching: predicate)
        .sorted { $0.startDate < $1.startDate }
}

Fetching a Single Event by Identifier

if let event = eventStore.event(withIdentifier: savedEventID) {
    print(event.title ?? "No title")
}

Reminders

Creating a Reminder

func createReminder(title: String, dueDate: Date) throws {
    let reminder = EKReminder(eventStore: eventStore)
    reminder.title = title
    reminder.calendar = eventStore.defaultCalendarForNewReminders()

    let dueDateComponents = Calendar.current.dateComponents(
        [.year, .month, .day, .hour, .minute],
        from: dueDate
    )
    reminder.dueDateComponents = dueDateComponents

    try eventStore.save(reminder, commit: true)
}

Fetching Reminders

Reminder fetches are asynchronous and return through a completion handler.

func fetchIncompleteReminders() async -> [EKReminder] {
    let predicate = eventStore.predicateForIncompleteReminders(
        withDueDateStarting: nil,
        ending: nil,
        calendars: nil
    )

    return await withCheckedContinuation { continuation in
        eventStore.fetchReminders(matching: predicate) { reminders in
            continuation.resume(returning: reminders ?? [])
        }
    }
}

Completing a Reminder

func completeReminder(_ reminder: EKReminder) throws {
    reminder.isCompleted = true
    try eventStore.save(reminder, commit: true)
}

Recurrence Rules

Use EKRecurrenceRule to create repeating events or reminders.

Simple Recurrence

// Every week, indefinitely
let weeklyRule = EKRecurrenceRule(
    recurrenceWith: .weekly,
    interval: 1,
    end: nil
)
event.addRecurrenceRule(weeklyRule)

// Every 2 weeks, ending after 10 occurrences
let biweeklyRule = EKRecurrenceRule(
    recurrenceWith: .weekly,
    interval: 2,
    end: EKRecurrenceEnd(occurrenceCount: 10)
)

// Monthly, ending on a specific date
let monthlyRule = EKRecurrenceRule(
    recurrenceWith: .monthly,
    interval: 1,
    end: EKRecurrenceEnd(end: endDate)
)

Complex Recurrence

// Every Monday and Wednesday
let days = [
    EKRecurrenceDayOfWeek(.monday),
    EKRecurrenceDayOfWeek(.wednesday)
]

let complexRule = EKRecurrenceRule(
    recurrenceWith: .weekly,
    interval: 1,
    daysOfTheWeek: days,
    daysOfTheMonth: nil,
    monthsOfTheYear: nil,
    weeksOfTheYear: nil,
    daysOfTheYear: nil,
    setPositions: nil,
    end: nil
)
event.addRecurrenceRule(complexRule)

Editing Recurring Events

When saving changes to a recurring event, specify the span:

// Change only this occurrence
try eventStore.save(event, span: .thisEvent)

// Change this and all future occurrences
try eventStore.save(event, span: .futureEvents)

Alarms

Attach alarms to events or reminders to trigger notifications.

// 15 minutes before
let alarm = EKAlarm(relativeOffset: -15 * 60)
event.addAlarm(alarm)

// At an absolute date
let absoluteAlarm = EKAlarm(absoluteDate: alertDate)
event.addAlarm(absoluteAlarm)

For reminder geofences, put an EKStructuredLocation and .enter / .leave proximity on an EKAlarm, then add it to the reminder. See references/eventkit-patterns.md for the full

Use this skill in a project

Open any RiverX project, search the Skills panel for dpearson2699/swift-ios-skills/eventkit, and install it in one click.

Use this skill