diff --git a/ios_pwa_push_notifications.md b/ios_pwa_push_notifications.md new file mode 100644 index 0000000..437476f --- /dev/null +++ b/ios_pwa_push_notifications.md @@ -0,0 +1,50 @@ +# Case Study: PWA Push Notifications on iOS (Cloudflare Workers) + +Implementing push notifications for iOS PWAs on Cloudflare Workers requires addressing several unique runtime and OS-specific constraints. + +## 🛠 Tech Stack +- **Framework**: Next.js (OpenNext) +- **Runtime**: Cloudflare Workers (Edge) +- **Database**: Cloudflare D1 +- **Library**: `web-push` + +## 🚀 Key Implementation Details + +### 1. Edge Runtime Compatibility (Crucial) +The standard `webPush.sendNotification()` relies on Node.js's `https` module, which is **not supported** in Cloudflare Workers. +**Solution**: Use `webPush.generateRequestDetails()` to create the encrypted payload, and then send it using the Edge-native `fetch`. + +```typescript +const requestDetails = await webPush.generateRequestDetails( + { endpoint, keys: { p256dh, auth } }, + JSON.stringify(payload) +); + +const response = await fetch(requestDetails.endpoint, { + method: 'POST', + headers: requestDetails.headers, + body: requestDetails.body // Note: This is a Buffer/ArrayBuffer +}); +``` + +### 2. VAPID Key Generation +VAPID keys must be generated using the native `crypto` module to avoid dependency issues in restricted environments. + +### 3. iOS PWA Manifest Requirements +For "Add to Home Screen" to trigger the correct push capabilities: +- `display: standalone` or `fullscreen` is required. +- The user **must** trigger the permission request via a direct interaction (e.g., clicking an "Enable" button). + +### 4. Background Cron Trigger +Since Next.js API routes on Cloudflare don't support long-running processes, a separate **Cron Worker** is needed to ping the API every minute. +- **Workflow**: `Cron Worker (triggers every minute)` -> `Ping /api/cron/reminders` -> `App logic checks D1` -> `App sends push via fetch`. + +### 5. Local Timezone Management +iOS background alerts are only useful if they fire at the user's local time. +- **The fix**: Explicitly capture the user's timezone on the client (`Intl.DateTimeFormat().resolvedOptions().timeZone`) and save it to the database. +- **Verification**: Ensure the backend API updates the `timezone` column alongside reminder settings to avoid defaulting to UTC. + +## 💡 Lessons Learned +- **Minute-Precision**: For hourly reminders, sync the *minute* value (e.g., :18 past the hour) between the UI and the backend checker. +- **Visual Feedback**: iOS users are often skeptical of web-based push. Providing a "Test & Sync" button that sends an immediate notification builds trust and verifies the connection. +- **Authentication**: Protect the cron endpoint with a `CRON_SECRET` header to prevent unauthorized trigger pings.