Github-Obsidian/02 - Tech Projects/Coding Projects/2026.03.Apple Reminders → N...

209 lines
5.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

Project Summary & Learnings  |  March 2026
# What We Built
A one-way automation that syncs incomplete tasks from Apple Reminders ("To Do" list) to a Notion database, with deduplication to prevent duplicate entries.
## Final Architecture
Reminders app closed
  → Shortcuts automation triggers
     → Scriptable script runs (EventKit)
        → Fetches all incomplete tasks from "To Do" list
           → POST to n8n webhook
              → Code node parses + structures tasks
                 → For each task:
                    → Query Notion by iCloud UID
                    → If not found → Create Notion page
                    → If found → Skip (no duplicate)
## Data Flow Per Task
- UID (unique identifier from EventKit)
- Title
- Due date (ISO 8601 format)
- Notes
- Status → hardcoded as "To Do" on creation
## Completion Sync (Reminders → Notion)
A second Scriptable script checks for tasks completed in the last 24 hours and updates their Notion status to "Done" via a separate n8n code node routing on action: "complete".
# Approaches Tried
## Option A — iCloud CalDAV Polling (abandoned)
The original plan was to poll iCloud's CalDAV server from n8n every 5 minutes. This is reliable and device-independent but hit a hard wall:
- Apple migrated modern Reminders lists to CloudKit (iOS 13+)
- CalDAV only exposes legacy lists — flagged as "Reminders ⚠️" in the calendar listing
- Custom lists like "To Do" created in the modern format are completely invisible to CalDAV
- No workaround exists — this is an Apple architectural decision
## Option B — Apple Shortcuts + n8n Webhook (partial)
Shortcuts has native access to modern Reminders but hit several limitations:
- No "when reminder is added" automation trigger exists in iOS
- Shortcuts bundles list variables into one concatenated string instead of firing per item
- Workarounds (Text actions, Set Variable, loop structures) all produced the same bundling behaviour
- Date + title pairing by index breaks when tasks have no due date — lists become misaligned
## Option C — Scriptable + EventKit (final solution)
Scriptable is a free iOS app that exposes native iPhone APIs including EventKit. Writing ~20 lines of JavaScript gave full programmatic access to Reminders with:
- Correct title per task
- ISO 8601 due date per task (null if not set)
- Unique identifier per task (essential for deduplication)
- Notes per task
Triggered via Shortcuts automation on Reminders app close.
# Technical Learnings
## Apple / iOS
- Modern Reminders (iOS 13+) use CloudKit — NOT CalDAV. CalDAV only works for legacy unconverted lists.
- Shortcuts cannot trigger on "reminder added" — only on app open/close or time-based.
- Shortcuts visual variable system is unreliable for extracting typed properties from list objects — it bundles values instead of iterating correctly.
- Scriptable + EventKit is the correct tool for programmatic Reminders access on iOS.
- EventKit's Reminder.allIncomplete([list]) returns full objects with all properties including identifier, dueDate, notes, completionDate.
## n8n
- n8n Cloud 2.9.4 Code nodes do not support $http, fetch, or this.getCredentials() — use helpers.httpRequest() instead.
- helpers.httpRequest() auto-parses JSON responses — do not wrap in JSON.parse().
- Test webhook URL (webhook-test/) only stays open for one call after clicking Execute Workflow — use production URL + activate toggle for persistent workflows.
- Notion node with 0 results outputs nothing downstream — IF nodes cannot branch on empty. Use a Code node with direct API calls to handle both search and create in one step.
- Custom HTTP methods like REPORT and PROPFIND are not available in the HTTP Request node dropdown — use a Code node with helpers.httpRequest() instead.
## Notion API
- Deduplication requires storing the source system's UID (iCloud UID) as a rich_text property in Notion.
- Status fields use { status: { name: "To Do" } } format — not select or text.
- Date fields require ISO 8601 format: { date: { start: "2026-03-10T00:00:00.000Z" } }.
- Integration must be explicitly connected to each database via Connections in Notion UI — otherwise API returns 401.
# Final Stack
- Reminders access: Scriptable (iOS)
- Automation trigger: Apple Shortcuts
- Workflow engine: n8n Cloud 2.9.4
- Task database: Notion API (2022-06-28)
# Future Extensions
- Notion → Reminders bidirectional sync (requires n8n polling + push notification trigger e.g. ntfy.sh or Pushover)
- Optimise to only sync tasks created/modified since last run (store last sync timestamp)
- Migrate to self-hosted n8n on Hetzner VPS (~€5/month) — migration is a half-day job: export JSON, re-authenticate credentials, update webhook URLs
- Add error handling and Slack/email alerts for failed syncs
Key credentials & config
n8n webhook: https://weisstrevor.app.n8n.cloud/webhook/c68fdc4a-0e56-4ae2-8c5f-14e339846cbe
Notion DB: 2872f572ad7d8089beb4f24380180a12
iCloud CalDAV: p25-caldav.icloud.com / Apple ID: 2004413068