91 lines
3.3 KiB
Markdown
91 lines
3.3 KiB
Markdown
|
|
# 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.
|