Add iOS PWA Push Notifications technical guide

This commit is contained in:
Avery Felts 2026-01-27 17:36:48 -07:00
parent cec4096e1f
commit c438e4f552

View File

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