macsync/docs/modules/ireminders.md

91 lines
3.3 KiB
Markdown
Raw Permalink Normal View History

# 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.