# iReminders (`IReminderSync`) ## Purpose Sync Reminders.app lists and items with the server, and apply web-initiated edits back via EventKit. ## Direction Bidirectional. Inbound: EventKit -> server. Outbound: `icloud.reminder_send_queue` -> `EKEventStore.save(EKReminder, commit:)` / `.remove(_, commit:)`. ## OS surface EventKit (`EKEntityType.reminder`). `Package.swift:84-86` links EventKit. Reader uses `EKEventStore.fetchReminders(matching:completion:)` (`@packages/ireminders/Sources/IReminderSync/Reader.swift:127`). ## Files - `Reader.swift` — `class ReminderReader` (line 66) with `requestAuthorization()` (line 80), `fetchCalendars()` (line 110), `fetchReminders(since:)` (line 124). - `APIClient.swift`: - `syncReminders(_:)` -> `POST /client/ireminders/sync` (`@packages/ireminders/Sources/IReminderSync/APIClient.swift:115`) - `getStats()` -> `GET /client/ireminders/stats` (line 128) - `getPendingSends()` -> `GET /client/ireminders/send-queue/pending` (line 142) - `reportSendResult(...)` -> `POST /client/ireminders/send-queue/:id/result` (line 168) - `Sender.swift` — reminder applier wired into a `SendQueueClient` at `@packages/ireminders/Sources/IReminderSync/SyncManager.swift:57`. - `SyncManager.swift` — read interval 300s (line 67), send interval 60s (line 57); persistence key `"ireminders"` (line 66). ## Timing - Read interval: **300s** (`SyncManager.swift:67`). - Outbound poll interval: **60s** (`SyncManager.swift:57`). ## Server surface - Entity tables: `icloud.reminders`, `icloud.reminder_send_queue` (`src/server/src/app/server.ts:45-46`). - Allowed actions: `create_reminder`, `update_reminder`, `delete_reminder` (`src/server/src/entities/reminderSendItem/types.ts:16-18`). - Client routes (`src/server/src/surfaces/client/ireminders.ts`): - `POST /client/ireminders/sync` (line 32) - `GET /client/ireminders/stats` (line 38) - `GET /client/ireminders/send-queue/pending` (line 42) - `POST /client/ireminders/send-queue/:id/result` (line 60) - Web routes (`src/server/src/surfaces/my/reminders.ts`): - `GET /my/reminders/stats` (line 45) - `GET /my/reminders/` (line 53) - `POST /my/reminders/` (line 73) - `PUT /my/reminders/:id` (line 87) - `DELETE /my/reminders/:id` (line 103) - Admin enqueue: `POST /admin/reminder-send-queue/enqueue` (`src/server/src/surfaces/admin/reminder-send-queue.ts:16`). ## Web surface - Tab: `/reminders` (`web/src/App.tsx:61`). - API helpers: `web/src/api/reminders.ts`. ## EventKit snippets ```swift let predicate = store.predicateForReminders(in: nil) store.fetchReminders(matching: predicate) { reminders in /* ... */ } // outbound: try store.save(reminder, commit: true) try store.remove(reminder, commit: true) ``` (Source: `@packages/ireminders/Sources/IReminderSync/Reader.swift:124-145`.) ## Known limitations - Last-write-wins, same as iCal. - Subtasks: EventKit exposes hierarchical reminders via `parent`/`children` but the schema flattens; check `entities/reminder/` if you need recursion. ## Tests (`@packages/ireminders/Tests/IReminderSyncTests/`) - `ReaderTests.swift` — Reader helpers that don't need EventKit data. - `SenderTests.swift` — applier dispatch (hermetic). Not covered: actual `fetchReminders` / `save` / `remove` (needs the OS), authorization prompts.