51 lines
2.6 KiB
Markdown
51 lines
2.6 KiB
Markdown
# 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.
|