Skip to content

Calendar Agent

easybar-calendar-agent owns EventKit.

It is responsible for:

  • requesting calendar access
  • observing changes
  • building normalized snapshots
  • grouping popup sections
  • handling event mutations
  • pushing updates to subscribers

Requests

{
  "requestID": "month-42",
  "command": "ping | version | fetch | subscribe | logs | create_event | update_event | delete_event",
  "query": {
    "startDate": "2026-03-29T00:00:00Z",
    "endDate": "2026-04-01T00:00:00Z"
  }
}

Notes:

  • requestID is optional and is echoed by every direct response for request correlation
  • query is required for fetch and subscribe
  • date range is inclusive/exclusive, must be forward and finite, and may span at most 366 days
  • section counts, filter arrays, text, identifiers, alerts, and mutation durations are bounded before EventKit work begins
  • filters are applied server-side to regular and birthday calendars

Responses

{
  "kind": "snapshot",
  "requestID": "month-42",
  "snapshot": { ... }
}

Other kinds:

  • pong
  • version
  • subscribed
  • log_subscribed
  • log_record
  • created
  • updated
  • deleted
  • error

Errors include a stable errorCode and a human-readable message:

{
  "kind": "error",
  "errorCode": "invalid_request",
  "message": "The calendar date range exceeds the supported maximum."
}

invalid_request means the request itself is unsupported and retrying the same payload cannot succeed. Other error codes describe permission, event lookup, writable-calendar, or mutation failures.

Snapshot

{
  "accessGranted": true,
  "permissionState": "authorized",
  "generatedAt": "2026-03-29T12:34:56Z",
  "events": [],
  "sections": []
}

Event fields

  • id
  • title
  • startDate
  • endDate
  • isAllDay
  • calendarName
  • calendarColorHex
  • location
  • url (direct EventKit URL, or the first URL EasyBar can extract from location or notes)
  • isHoliday
  • hasAlert
  • travelTimeSeconds

Behavior notes

  • the month client derives its preload radius from the shared 366-day request limit instead of using a hard-coded radius
  • EasyBarKit assigns request identifiers to subscriptions and ignores delayed direct responses that belong to an older request; unsolicited broadcast snapshots intentionally omit an identifier
  • no access returns an empty snapshot
  • birthdays are separated and use the same calendar filters as regular events
  • occurrence ids are deterministic even when EventKit omits an event identifier
  • relative and absolute alarms are normalized into visible lead times
  • sections are optional, day-bucketed once, and clamp multi-day display times to each section day
  • EasyBarKit treats invalid_request as permanent for the exact rejected subscription: it logs the rejection once, suspends reconnects, and retains the last valid snapshot
  • when the subscription request or socket configuration changes, EasyBar clears the permanent block and reconnects immediately

Event travel-time bridge

EventKit does not expose a public, typed API for reading or writing an event's native travel-time value. EasyBar therefore accesses the runtime travelTime property through EventKitTravelTimeAdapter, with the dynamic Objective-C work isolated in CEasyBarEventKitBridge.

The Objective-C boundary is intentional:

  • KVC access can raise an Objective-C exception, which Swift do/catch cannot catch.
  • the bridge checks that the runtime getter or setter exists before using it
  • Objective-C catches any KVC exception and reports failure to Swift instead of crashing the agent
  • the Swift adapter rejects non-finite, negative, zero-on-read, or excessively large values

A pure Swift KVC implementation might work on the current EventKit implementation, but it would lose the exception boundary. Calling the Objective-C method implementation directly from Swift would instead introduce unsafe runtime calling conventions and would still not catch Objective-C exceptions.

The bridge is a separate Swift Package Manager target because a target cannot mix Swift and Objective-C sources. It can be removed if travel-time support is removed, or if EventKit gains a public typed API that covers both reading and writing the native value.

Boundary

The calendar agent collects calendar data and performs calendar mutations.

EasyBarKit builds calendar presentation state for the full EasyBar product. EasyBar Native does not register or depend on the Calendar built-in.