iOS
PWA
Push Notifications
Web Development
Mobile Technology

Sending Push Notifications to iOS from PWA

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Sending push notifications to an iOS Progressive Web App is possible today, but only under specific conditions. The important shift is that iOS supports standards-based Web Push for Home Screen web apps, not generic push for any page open in Safari. That means the PWA must be installable, launched from the Home Screen, and registered with a service worker before push permissions and subscriptions make sense.

What iOS Actually Supports

On modern iOS versions, Web Push support is tied to Home Screen web apps. In practice, that means:

  • the site must be served over HTTPS
  • the app must include a web manifest
  • the app must be added to the Home Screen
  • the user must open the installed web app and grant notification permission there
  • a service worker must handle push events

This is not the same as shipping an APNs-enabled native app. The browser-facing APIs are still the Push API, Notifications API, and service workers.

The Overall Flow

A normal implementation has four parts:

  1. the PWA registers a service worker
  2. the installed web app requests notification permission
  3. the app creates a PushSubscription and sends it to your backend
  4. your backend sends Web Push messages to that subscription endpoint

The browser and Apple infrastructure handle the delivery path behind the scenes. Your server does not talk to APNs using the native-app provider API for this workflow.

Register the Service Worker

javascript
1if ("serviceWorker" in navigator) {
2  navigator.serviceWorker.register("/sw.js")
3    .then(() => console.log("service worker registered"))
4    .catch(console.error);
5}

The service worker handles incoming push events and notification clicks.

Example sw.js:

javascript
1self.addEventListener("push", event => {
2  const data = event.data ? event.data.json() : { title: "Notification", body: "No payload" };
3
4  event.waitUntil(
5    self.registration.showNotification(data.title, {
6      body: data.body,
7      icon: "/icons/icon-192.png",
8      data: data.url || "/"
9    })
10  );
11});
12
13self.addEventListener("notificationclick", event => {
14  event.notification.close();
15  event.waitUntil(clients.openWindow(event.notification.data || "/"));
16});

Ask for Permission and Create a Subscription

Permission should be requested only in a user-initiated flow, such as tapping an "Enable notifications" button.

javascript
1async function subscribeToPush() {
2  const registration = await navigator.serviceWorker.ready;
3  const permission = await Notification.requestPermission();
4
5  if (permission !== "granted") {
6    throw new Error("Notification permission not granted");
7  }
8
9  const subscription = await registration.pushManager.subscribe({
10    userVisibleOnly: true,
11    applicationServerKey: urlBase64ToUint8Array("YOUR_PUBLIC_VAPID_KEY")
12  });
13
14  await fetch("/api/push-subscriptions", {
15    method: "POST",
16    headers: { "Content-Type": "application/json" },
17    body: JSON.stringify(subscription)
18  });
19}

The backend stores the subscription and later uses it to send notifications.

Server-Side Sending

A common Node.js backend uses the web-push package.

javascript
1import webpush from "web-push";
2
3webpush.setVapidDetails(
4  "mailto:[email protected]",
5  process.env.VAPID_PUBLIC_KEY,
6  process.env.VAPID_PRIVATE_KEY
7);
8
9async function sendPush(subscription) {
10  const payload = JSON.stringify({
11    title: "Build complete",
12    body: "Your export is ready.",
13    url: "/downloads"
14  });
15
16  await webpush.sendNotification(subscription, payload);
17}

This is standard Web Push server behavior. The subscription endpoint tells the push service where to route the notification.

iOS-Specific Caveats

The most important caveat is that notification support is tied to the installed Home Screen app experience. If you test only in a normal Safari tab, you may conclude push does not work even though the actual PWA path is correct.

Also keep expectations realistic:

  • not all legacy iOS versions support this
  • the user must explicitly grant permission
  • background behavior is still subject to platform rules and power management

So the right test path is always the installed PWA, not just the website in a tab.

Common Pitfalls

The most common mistake is trying to request push permission from a normal Safari tab and expecting Home Screen web app behavior.

Another mistake is assuming you need the native APNs provider API. For PWA web push, the browser-facing flow is standards-based Web Push with service workers and push subscriptions.

Developers also often forget that permission prompts should be triggered by a user action. Calling them arbitrarily on page load is poor UX and often ineffective.

Finally, do not skip manifest and service-worker setup. Without an installable PWA foundation, iOS push support is not the feature set you think you are targeting.

Summary

  • iOS PWA push works through standards-based Web Push for Home Screen web apps.
  • The PWA must be installable, served over HTTPS, and registered with a service worker.
  • Permission and subscription should happen from the installed PWA, not just a Safari tab.
  • Your backend sends notifications using the stored PushSubscription and Web Push protocol.
  • Test the actual installed PWA flow, because that is where iOS support applies.

Course illustration
Course illustration

All Rights Reserved.