Web push on iPhone and iPad
What iOS needs for web push: version 16.4, a Home Screen web app, a standalone manifest and a prompt from a tap. With detection code and a service worker.
iPhones and iPads get web push for sites that the user has added to the Home Screen, since iOS and iPadOS 16.4. It is the same Push API, Notifications API and service worker that Chrome and Firefox use, with three extra conditions. Most “web push does not work on iOS” reports are one of them.
The three conditions
- iOS or iPadOS 16.4 or later.
- Opened from the Home Screen. The user taps Share, then Add to Home Screen, and opens the site from that icon. In a Safari tab the page has no
PushManagerat all. - A manifest with
displayset tostandaloneorfullscreen. Without it, the Home Screen icon opens a Safari tab and condition 2 fails.
And one rule for the prompt: Notification.requestPermission() must run from a tap. On page load it is ignored.
The manifest
{
"name": "Example",
"short_name": "Example",
"id": "/",
"start_url": "/",
"display": "standalone",
"icons": [{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" }]
}
<link rel="manifest" href="/manifest.webmanifest" />
The id matters on iOS: it identifies the installed web app, and Focus settings follow it across the user’s devices.
Detect, then guide the user
Detect features, not browsers. On an iPhone without PushManager, the useful answer is a short “add to Home Screen” hint:
const isIOS = /iPhone|iPad|iPod/.test(navigator.userAgent) || (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
const standalone = window.matchMedia("(display-mode: standalone)").matches || navigator.standalone === true;
if ("PushManager" in window && "serviceWorker" in navigator) {
showEnableButton();
} else if (isIOS && !standalone) {
showHint("To get notifications, tap Share, then Add to Home Screen, and open the app from there.");
}
Ask from a tap
enableButton.addEventListener("click", async () => {
const permission = await Notification.requestPermission(); // first line in the handler
if (permission !== "granted") return;
const reg = await navigator.serviceWorker.ready;
const sub = await reg.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: VAPID_PUBLIC_KEY });
await fetch("/api/push/subscribe", { method: "POST", body: JSON.stringify(sub) });
});
Fetch anything you need, such as the VAPID key, before the tap. An await fetch() between the tap and requestPermission() can lose the gesture.
The service worker
self.addEventListener("push", (event) => {
const { title, body, url } = event.data.json();
event.waitUntil(self.registration.showNotification(title, { body, data: { url } }));
});
self.addEventListener("notificationclick", (event) => {
event.notification.close();
event.waitUntil(clients.openWindow(event.notification.data.url || "/"));
});
Always show a notification for every push. Safari expects each push to produce one (userVisibleOnly: true), and silent pushes are not supported.
Badges
iOS 16.4 also added the Badging API for Home Screen web apps: navigator.setAppBadge(3) and navigator.clearAppBadge(), from the page or from the service worker’s push handler.
Sending
The subscription’s endpoint is on web.push.apple.com. Send to it exactly as to Chrome’s or Firefox’s: an encrypted body, a VAPID Authorization header, a TTL. If your server has an allow list for outgoing requests, add *.push.apple.com. Apple is strict about the VAPID sub claim: give a real mailto: or https: value. A 403 means the VAPID header failed; a 404 or 410 means the subscription is gone. All the codes are in web push errors.
Things that still differ from Android and desktop
- Each Home Screen install has its own permission and subscription, separate from Safari.
- If the user removes the icon, the subscription goes with it; your next send gets a 410.
- In the EU, Apple’s iOS 17.4 changes briefly removed Home Screen web apps, then restored them; test on current versions before relying on any one behaviour.
With SignOps
SignOps Push sends web pushes to Safari, Chrome, Edge and Firefox with VAPID keys it makes for your app, removes subscriptions that answer 404 or 410, and sends to the user’s Android and iOS apps from the same call. The web push guide has the iPhone steps, and the web push tester sends a test push to the device you open it on.
Sources: WebKit: Web Push for Web Apps on iOS and iPadOS, MDN: Push API, MDN: Badging API.