Safe Rust bindings for Apple's EventKit framework on macOS.
Status: v0.2.1 completes the symbol-level audit at 100% coverage by adding
EKObjectstate wrappers andEKParticipantScheduleStatusto the existing EventKit surfaces. Extension-onlyEKVirtualConferenceProviderrequest hooks remain documented inCOVERAGE.md.
use eventkit::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let store = EKEventStore::new()?;
println!("store id: {}", store.event_store_identifier()?);
println!("event access: {:?}", EKEventStore::authorization_status(EKEntityType::Event));
let sources = store.sources()?;
let calendars = store.calendars_for_entity_type(EKEntityType::Event)?;
println!("sources: {}", sources.len());
println!("event calendars: {}", calendars.len());
Ok(())
}EKEventStorewrappers for authorization, source-scoped stores, source/calendar lookup, event/reminder predicates, save/remove flows, commit/reset, and source refresh.- Live
EKObjectwrappers forhas_changes,is_new,reset,rollback, andrefresh, plusas_object_inhelpers onEKEvent,EKReminder, andEKCalendarDraft. - Rich
EKEvent+EKRemindersnapshots with alarms, recurrence rules, participants, organizers, structured locations, date components, andEKParticipantScheduleStatus. EKCalendar+EKSourcesnapshots, plus unsavedEKCalendarDraftround-trips for safe headless testing.EKRecurrenceRule,EKAlarm,EKStructuredLocation, and virtual conference descriptor round-trips.- One example and one integration test per logical area.
Enable the async Cargo feature for Future-based wrappers around EventKit's
completion-handler APIs:
[dependencies]
eventkit = { version = "0.3", features = ["async"] }use eventkit::async_api::AsyncEventStore;
use eventkit::event_store::{EKEventStore, EKReminderPredicate};
async fn example() -> Result<(), Box<dyn std::error::Error>> {
let store = AsyncEventStore::new(EKEventStore::new()?);
let granted = store.request_full_access_to_reminders().await?;
if granted {
let reminders = store.fetch_reminders(&EKReminderPredicate::new())?.await?;
println!("found {} reminder(s)", reminders.len());
}
Ok(())
}The async API is executor-agnostic — it works with tokio, async-std, smol,
pollster, or any other runtime. See [async_api] in the crate docs and
examples/12_async_access.rs for a runnable example.
Tier-2 note:
EKEventStorechange notifications (multi-fire stream) are not yet wrapped; they will appear in a futureStream-based Tier-2 release.
COVERAGE.md tracks the v0.2.1 audit against the macOS 26.2 EventKit.framework headers and calls out the intentionally skipped APIs:
- deprecated legacy initializers / AddressBook integrations,
- cross-framework convenience APIs that would force a
MapKitdependency, - extension-only
EKVirtualConferenceProvidersubclass hooks.
EventKit.framework access is gated by macOS privacy settings. The shipped examples and tests are intentionally headless-safe: they favor non-mutating lookups and JSON round-trips, and they tolerate zero visible calendars/sources.
Run the store smoke example with:
cargo run --example 01_event_store_smokeRun the full example suite with:
for ex in examples/*.rs; do cargo run --example "$(basename "$ex" .rs)"; doneLicensed under either of Apache-2.0 or MIT at your option.