Skip to content

Instantly share code, notes, and snippets.

@bnski
Created September 25, 2025 14:57
Show Gist options
  • Select an option

  • Save bnski/bf6829da487fd23e41dcbfd7977f32c6 to your computer and use it in GitHub Desktop.

Select an option

Save bnski/bf6829da487fd23e41dcbfd7977f32c6 to your computer and use it in GitHub Desktop.
Mac OS Calendars + Reminders to JSON

Calendar + Reminders Export to JSON

Build: swiftc calendar_dump.swift -o calendar_dump

Run: ./calendar_dump (accept Calendar and Reminders access)

Output: events.json in the current directory.

Includes today's events + incomplete reminders due by end of day.

import EventKit
let eventStore = EKEventStore()
let now = Date()
let startOfDay = Calendar.current.startOfDay(for: now)
let endOfDay = Calendar.current.date(byAdding: .day, value: 1, to: startOfDay)!
func iso(_ date: Date?) -> String? {
guard let date = date else { return nil }
let formatter = ISO8601DateFormatter()
return formatter.string(from: date)
}
eventStore.requestFullAccessToEvents { granted, _ in
guard granted else {
print("Access denied to Calendar")
exit(1)
}
var results: [[String: Any]] = []
// MARK: Calendar Events
let calendars = eventStore.calendars(for: .event)
let predicate = eventStore.predicateForEvents(withStart: startOfDay, end: endOfDay, calendars: calendars)
let events = eventStore.events(matching: predicate).sorted { $0.startDate < $1.startDate }
for event in events {
let timezoneId = event.timeZone?.identifier ?? TimeZone.current.identifier
results.append([
"type": "event",
"calendar": event.calendar.title,
"title": event.title ?? "Untitled",
"start": iso(event.startDate) ?? "",
"end": iso(event.endDate) ?? "",
"timezone": timezoneId,
"allDay": event.isAllDay,
"highPriority": false
])
}
// MARK: Reminders
eventStore.requestFullAccessToReminders { granted, _ in
guard granted else {
print("Access denied to Reminders")
exit(1)
}
// Include reminders due today and any past due (incomplete, due <= endOfDay)
let predicate = eventStore.predicateForIncompleteReminders(withDueDateStarting: nil, ending: endOfDay, calendars: nil)
eventStore.fetchReminders(matching: predicate) { reminders in
reminders?.forEach { reminder in
let isHigh = reminder.priority > 0 && reminder.priority <= 3
let dueDate = reminder.dueDateComponents?.date
let isOverdue = {
guard let d = dueDate else { return false }
return d < now
}()
let dueToday = {
guard let d = dueDate else { return false }
return d >= startOfDay && d < endOfDay
}()
results.append([
"type": "reminder",
"calendar": reminder.calendar.title,
"title": reminder.title,
"start": iso(reminder.startDateComponents?.date) ?? "",
"due": iso(reminder.dueDateComponents?.date) ?? "",
"priority": reminder.priority,
"highPriority": isHigh,
"overdue": isOverdue,
"completed": reminder.isCompleted,
"dueToday": dueToday
])
}
// Write to events.json
let fileURL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath).appendingPathComponent("events.json")
if let json = try? JSONSerialization.data(withJSONObject: results, options: .prettyPrinted) {
do {
try json.write(to: fileURL)
print("✅ events.json written to \(fileURL.path)")
} catch {
print("❌ Failed to write file: \(error)")
}
} else {
print("❌ Failed to serialize JSON")
}
exit(0)
}
}
}
RunLoop.current.run()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment