CtrlK
BlogDocsLog inGet started
Tessl Logo

dpearson2699/swift-ios-skills

Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.

71

Quality

89%

Does it follow best practices?

Impact

No eval scenarios have been run

SecuritybySnyk

Advisory

Suggest reviewing before use

Overview
Quality
Evals
Security
Files

eventkit-patterns.mdskills/eventkit/references/

EventKit Extended Patterns

Overflow reference for the eventkit skill. Contains advanced patterns that exceed the main skill file's scope.

Contents

  • SwiftUI Calendar Integration
  • Advanced Predicate Queries
  • Batch Operations
  • Calendar Management
  • Reminder Workflows
  • EventKitUI in SwiftUI

SwiftUI Calendar Integration

Observable Event Manager

import EventKit
import SwiftUI

@Observable
@MainActor
final class CalendarManager {
    let eventStore = EKEventStore()
    var events: [EKEvent] = []
    var authorizationStatus: EKAuthorizationStatus = .notDetermined

    func requestAccess() async {
        do {
            let granted = try await eventStore.requestFullAccessToEvents()
            authorizationStatus = granted ? .fullAccess : .denied
            if granted { fetchThisWeekEvents() }
        } catch {
            authorizationStatus = .denied
        }
    }

    func fetchThisWeekEvents() {
        let calendar = Calendar.current
        let start = calendar.startOfDay(for: Date())
        let end = calendar.date(byAdding: .day, value: 7, to: start)!

        let predicate = eventStore.predicateForEvents(
            withStart: start,
            end: end,
            calendars: nil
        )
        events = eventStore.events(matching: predicate)
            .sorted { $0.startDate < $1.startDate }
    }

    func observeChanges() {
        NotificationCenter.default.addObserver(
            forName: .EKEventStoreChanged,
            object: eventStore,
            queue: .main
        ) { [weak self] _ in
            self?.fetchThisWeekEvents()
        }
    }
}

SwiftUI View with Calendar Events

struct CalendarEventsView: View {
    @State private var manager = CalendarManager()

    var body: some View {
        List(manager.events, id: \.eventIdentifier) { event in
            VStack(alignment: .leading) {
                Text(event.title)
                    .font(.headline)
                Text(event.startDate, style: .date)
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
        }
        .task {
            await manager.requestAccess()
            manager.observeChanges()
        }
        .overlay {
            if manager.events.isEmpty {
                ContentUnavailableView(
                    "No Events",
                    systemImage: "calendar",
                    description: Text("No events this week.")
                )
            }
        }
    }
}

Advanced Predicate Queries

Events in a Specific Calendar

func fetchEvents(in calendar: EKCalendar, range: DateInterval) -> [EKEvent] {
    let predicate = eventStore.predicateForEvents(
        withStart: range.start,
        end: range.end,
        calendars: [calendar]
    )
    return eventStore.events(matching: predicate)
}

Completed Reminders in a Date Range

func fetchCompletedReminders(from start: Date, to end: Date) async -> [EKReminder] {
    let predicate = eventStore.predicateForCompletedReminders(
        withCompletionDateStarting: start,
        ending: end,
        calendars: nil
    )

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

Enumerating Events Efficiently

For large date ranges, use enumerateEvents to process events one at a time without loading all into memory.

func processAllEvents(from start: Date, to end: Date) {
    let predicate = eventStore.predicateForEvents(
        withStart: start,
        end: end,
        calendars: nil
    )

    eventStore.enumerateEvents(matching: predicate) { event, stop in
        // Process each event
        if event.title.contains("Cancel") {
            stop.pointee = true // Stop enumeration early
        }
    }
}

Batch Operations

Creating Multiple Events Efficiently

Use commit: false for individual saves, then commit once at the end.

func createEvents(from entries: [(String, Date, Date)]) throws {
    for (title, start, end) in entries {
        let event = EKEvent(eventStore: eventStore)
        event.title = title
        event.startDate = start
        event.endDate = end
        event.calendar = eventStore.defaultCalendarForNewEvents
        try eventStore.save(event, span: .thisEvent, commit: false)
    }
    try eventStore.commit()
}

Deleting Events in Bulk

func deleteEvents(_ events: [EKEvent]) throws {
    for event in events {
        try eventStore.remove(event, span: .thisEvent, commit: false)
    }
    try eventStore.commit()
}

Resetting Unsaved Changes

// Discard all uncommitted changes
eventStore.reset()

Calendar Management

Creating a Custom Calendar

func createCalendar(name: String, color: CGColor) throws -> EKCalendar {
    let calendar = EKCalendar(for: .event, eventStore: eventStore)
    calendar.title = name
    calendar.cgColor = color

    // Find a local source (iCloud, local, etc.)
    if let localSource = eventStore.sources.first(where: {
        $0.sourceType == .local
    }) {
        calendar.source = localSource
    } else if let defaultSource = eventStore.defaultCalendarForNewEvents?.source {
        calendar.source = defaultSource
    }

    try eventStore.saveCalendar(calendar, commit: true)
    return calendar
}

Listing Calendars by Type

// All event calendars
let eventCalendars = eventStore.calendars(for: .event)

// All reminder calendars
let reminderCalendars = eventStore.calendars(for: .reminder)

// Only writable calendars
let writableCalendars = eventCalendars.filter { $0.allowsContentModifications }

Reminder Workflows

Reminder with Location-Based Alarm

func createLocationReminder(
    title: String,
    latitude: Double,
    longitude: Double
) throws {
    let reminder = EKReminder(eventStore: eventStore)
    reminder.title = title
    reminder.calendar = eventStore.defaultCalendarForNewReminders()

    let location = EKStructuredLocation(title: "Target Location")
    location.geoLocation = CLLocation(latitude: latitude, longitude: longitude)
    location.radius = 200 // meters

    let alarm = EKAlarm()
    alarm.structuredLocation = location
    alarm.proximity = .enter  // .enter or .leave
    reminder.addAlarm(alarm)

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

Reminder with Priority

let reminder = EKReminder(eventStore: eventStore)
reminder.title = "Important Task"
reminder.priority = 1  // 1-4: High, 5: Medium, 6-9: Low, 0: None
reminder.calendar = eventStore.defaultCalendarForNewReminders()

EventKitUI in SwiftUI

UIViewControllerRepresentable for Event Editor

import SwiftUI
import EventKitUI

struct EventEditView: UIViewControllerRepresentable {
    let eventStore: EKEventStore
    @Binding var isPresented: Bool

    func makeUIViewController(context: Context) -> EKEventEditViewController {
        let editor = EKEventEditViewController()
        editor.eventStore = eventStore
        editor.editViewDelegate = context.coordinator
        return editor
    }

    func updateUIViewController(
        _ uiViewController: EKEventEditViewController,
        context: Context
    ) {}

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    class Coordinator: NSObject, EKEventEditViewDelegate {
        let parent: EventEditView

        init(_ parent: EventEditView) {
            self.parent = parent
        }

        func eventEditViewController(
            _ controller: EKEventEditViewController,
            didCompleteWith action: EKEventEditViewAction
        ) {
            parent.isPresented = false
        }
    }
}

Usage in SwiftUI

struct CalendarView: View {
    @State private var showEditor = false
    let eventStore = EKEventStore()

    var body: some View {
        Button("Add Event") { showEditor = true }
            .sheet(isPresented: $showEditor) {
                EventEditView(
                    eventStore: eventStore,
                    isPresented: $showEditor
                )
            }
    }
}

skills

CHANGELOG.md

README.md

tile.json