All Articles
CategoryiOS
Reading Time
14 min read
Published
2025-12-16
Word Count
3,411words

Grab a coffee — this one is a deep dive!

iOS PWA and Web Push: Today's Real Limits

Summary

Learn what web push, the Badging API, and Add to Home Screen actually allow on iOS Safari today — and what they still don't — straight from WebKit and Apple's own docs, with zero invented numbers.

  • Safari 16.4 (March 2023) brought standard W3C Web Push, the Badging API, and Focus integration to Home Screen web apps on iOS.
  • Push permission can only be requested via direct user interaction (tap/click); an automatic prompt on page load doesn't work on iOS.
  • iOS has no Chromium-style beforeinstallprompt event; installation is always a manual Share menu step, and iOS 26 loosened the manifest requirement too.
  • Apple's official docs give no numeric delivery rate, only a 'delivery isn't guaranteed' warning; (September 2026 update:) Safari 27.0 progressed not on push but on performance, via the Service Worker Static Routing API.
iOS PWA and Web Push: Today's Real Limits

Can you add a web app (PWA) to the Home Screen on iOS and send it notifications without ever touching the App Store? Short answer: yes, since 2023 — but with clear boundaries. This post covers, straight from WebKit's and Apple's own docs and without invented percentages or anecdotes, what Safari actually allows today, what it still doesn't, and which product type these limits fit.

💡 Pro Tip: Before setting up web push on iOS, test one thing: try showing the permission prompt before the app is added to the Home Screen. You won't see it — Web Push only exists for installed web apps, and the request must be tied directly to a tap; it can't fire on page load or in the background.

Table of Contents

What PWA can and can't do on iOS

Safari 16.4 (March 27, 2023) brought standards-based Web Push to Home Screen web apps on iOS and iPadOS. WebKit's own announcement is unambiguous: "Today marks the release of iOS and iPadOS 16.4 beta 1... comes support for Web Push... for Home Screen web apps." It's not new technology either — WebKit notes it's "the same W3C-standards-based Web Push" that macOS Ventura got the previous fall (2022) with Safari 16.1.

The web app capabilities that shipped with 16.4 aren't just push:

  • Badging API: navigator.setAppBadge() and navigator.clearAppBadge() let you show a numeric badge on the Home Screen icon.
  • Manifest id field: makes it possible to tell apart multiple web app installs from the same origin and enables Focus sync.
  • Focus integration: Home Screen web app notifications integrate with Focus, so users can precisely configure when and where they receive them.
  • Native notification surface: in WebKit's words, web app notifications "work exactly the same as notifications from other apps" — including the Lock Screen, Notification Center, and a paired Apple Watch.

The manifest id field looks like this in practice:

json
1{
2 "id": "/app/",
3 "name": "Example Web App",
4 "start_url": "/app/?source=homescreen",
5 "display": "standalone"
6}

You don't need a separate permission request to use the Badging API; you can call it directly in a web app that already has push permission:

js
1// Show unread item count on the Home Screen icon
2if ("setAppBadge" in navigator) {
3 navigator.setAppBadge(unreadCount).catch((error) => {
4 console.error("Failed to update badge:", error);
5 });
6}
7 
8// Clear the badge once the user has read everything
9if ("clearAppBadge" in navigator) {
10 navigator.clearAppBadge();
11}

WebKit scopes Badging API support to Home Screen web apps: setAppBadge and clearAppBadge change the count while the app is open in the foreground, or while it's handling push events in the background. Don't rely on this from an ordinary browser-tab page.

Still missing on iOS: the automatic install nudge Chromium browsers impose. MDN's installability guide explains that on Chromium, manifest fields including name/short_name, 192px+512px icons, start_url, and display/display_override are required and trigger the beforeinstallprompt event — but this criteria set doesn't apply to Safari/iOS, and the event doesn't exist there at all. Installation always depends on the user manually tapping "Add to Home Screen" in the Share menu.

iOS/iPadOS 26, released September 15, 2025, added one more line here: Apple's release notes say "Added support for any website to become a web app on iOS or iPadOS" — even an ordinary site without a manifest can now be added and behave as a web app. The same release also fixed an issue where "Add to Home Screen" could fail to load page data and block creating a new web app.

The table below places the same capability side by side on iOS Safari versus Chromium — item by item, instead of an unsourced "iOS is always restricted" generalization:

Capability
iOS Safari (16.4+)
Chromium browsers
Install trigger
Manual only, via Share menu → Add to Home Screen
Automatic nudge banner via the beforeinstallprompt event
Web app prerequisite
A manifest (display: standalone/fullscreen) was required for web app behavior; since iOS 26, a manifest-less site can also become a web app
Fields including name/short_name, 192px+512px icons, start_url, display/display_override are required
Push permission trigger
Direct user interaction only (tap/click handler)
Can also be requested on page load (not recommended as best practice, but technically possible)
Notification surface
Identical to native notifications (Lock Screen, Notification Center, Apple Watch)
Depends on the platform's notification center, varies by browser
Badge
Badging API (setAppBadge/clearAppBadge), 16.4+
Badging API support varies by browser

Web push: the install prerequisite, the permission flow, and the delivery guarantee

The install order is strict: the web app must first be added to the Home Screen, and only then can push permission be requested — and that request must fire "in response to direct user interaction." An automatic prompt on page load doesn't work on iOS; the user has to tap something like an "enable notifications" button.

Nothing extra to learn server-side: Apple's developer documentation confirms existing W3C Push API code works the same in "Safari version 16.0 and later" as in other browsers. If your push implementation is already standards-compliant (feature detection, not browser sniffing), you don't need a separate code path for iOS.

Watch out for inflated delivery numbers. Apple's framework states it plainly: "The system makes every attempt to deliver local and remote notifications in a timely manner, but delivery isn't guaranteed." This isn't iOS-specific — it's a system-wide caveat for all notification types. Any "X% delivery rate" figure isn't in Apple's official docs; question the source of a claim like that.

js
1// The web app is already added to the Home Screen, and the user tapped a button
2async function subscribeToPush() {
3 const registration = await navigator.serviceWorker.ready;
4 
5 // Push permission only works on iOS when this function is called
6 // from inside a "click" event handler — it can't fire in the background
7 const permission = await Notification.requestPermission();
8 if (permission !== "granted") return null;
9 
10 const subscription = await registration.pushManager.subscribe({
11 userVisibleOnly: true,
12 applicationServerKey: VAPID_PUBLIC_KEY,
13 });
14 
15 return subscription; // send it to your server, it will be delivered via APNs
16}

Service worker, caching, and lifecycle constraints

The technical backbone of a PWA is the service worker, which MDN defines as running "on a separate thread" — offline caching and background tasks run through it, letting computationally heavy work run in the background without blocking the main thread.

For caching, the standard route is the Cache API: MDN describes it as providing "persistent storage for Request/Response object pairs," and most apps add resources to the cache inside install or fetch handlers — so a cache-first strategy is, at the code level, tied to those two events.

js
1const CACHE_NAME = "app-shell-v1";
2const ASSETS = ["/", "/styles.css", "/app.js", "/offline.html"];
3 
4self.addEventListener("install", (event) => {
5 event.waitUntil(
6 caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS)),
7 );
8});
9 
10self.addEventListener("fetch", (event) => {
11 event.respondWith(
12 caches
13 .match(event.request)
14 .then((cached) => cached || fetch(event.request)),
15 );
16});

The real limit isn't cache size — it's the worker's lifetime. MDN's offline/background guide is explicit: "This doesn't mean service workers run all the time: browsers may stop service workers when they think it is appropriate. For example, if a service worker has been inactive for a while, it will be stopped." The guide notes "how long is too long" varies by browser, giving concrete Chrome thresholds: 30 seconds idle, 30 seconds of synchronous JavaScript, or a waitUntil() promise running past 5 minutes all shut the worker down. waitUntil() isn't a guarantee either: if the operation runs too long, the worker stops and the handler restarts from scratch on the next sync event.

Practical consequence: don't rely solely on the Cache API or IndexedDB for critical data without a server-side backup. For sync needs that must fire over a long stretch without user interaction (a bulk update at midnight, say), it's safer to design it as "refreshes when the user opens the app" rather than as a guaranteed platform capability.

Measuring Add to Home Screen conversion

Measuring installs is straightforward in Chromium: the beforeinstallprompt event fires, you listen for it, and read the accept/reject decision off the event object. This event doesn't exist at all on iOS — MDN's installability criteria are written entirely for browsers where the event exists; no equivalent is defined for Safari.

Consequence: you can't measure "Add to Home Screen" conversion on iOS with a direct API. The only reliable signal is whether the app is already running standalone versus in a browser tab:

js
1function isRunningAsInstalledApp() {
2 // navigator.standalone returns true in web app mode on iOS Safari
3 const iosStandalone = window.navigator.standalone === true;
4 // Other browsers use the display-mode media query
5 const displayModeStandalone = window.matchMedia(
6 "(display-mode: standalone)",
7 ).matches;
8 return iosStandalone || displayModeStandalone;
9}

Add this signal to your analytics and track "how many sessions started in standalone mode" — but that shows an already-installed user's later sessions, not the install moment itself. The instant the user taps "Add to Home Screen" never leaks into JavaScript on iOS at all.

Which product type PWA is enough for, and which it isn't

Product need
Met by PWA on iOS?
Source/reasoning
Sending notifications (order, reminder, campaign)
Yes
Web Push + Badging + Focus integration, on the same surface as native
Offline page viewing, basic form filling
Yes
Service worker + Cache API, install/fetch events
Continuous background location tracking
No
The service worker lifecycle doesn't support this kind of sustained background task
Bluetooth device pairing/integration
No
Web Bluetooth isn't supported on iOS Safari
Distributing without tying installation to the App Store
Yes
Add to Home Screen, outside the App Store review process
Automatic install nudge (banner/prompt)
No
beforeinstallprompt doesn't exist on iOS, installation is entirely manual

For notification-heavy, moderately complex products (a news app, cart reminders, appointment notifications), the table is clear: PWA is now a genuine native alternative. For products needing deep hardware integration or continuous background sync, the service worker lifecycle limits MDN documents (on MDN's Chrome thresholds, a 30s-idle shutdown and a 5-minute waitUntil() ceiling, after which interrupted work restarts from scratch) fall short.

Ask yourself: is your product's core value triggered by a notification (a new message, a price drop, an appointment reminder)? Is a session typically minutes, or does the app need to stay open in the background for hours? "Notification + short session" means a PWA's scope (service worker + Push API + Badging API) is largely sufficient. "Continuous background + hardware" pushes you back toward the App Store — and there, rather than skipping PWA entirely, the hybrid approach below may be cheaper.

The hybrid path: PWA + a thin native shell

Two developments have recently cut distribution friction. First, iOS 26's "any site can be a web app" expansion — sites without a manifest can now be added and behave like one. Second, since Safari 16.4, third-party browsers can also offer "Add to Home Screen" from the Share menu; in WebKit's words, "third-party web browsers can offer 'Add to Home Screen' in the Share menu."

Together these make a distribution strategy outside the App Store more defensible: keep the core experience (notifications, offline, basic interaction) in the PWA, and add a thin native shell only for what's genuinely necessary — deep hardware access, in-app purchases via StoreKit 2, platform-specific deep linking. This differs from writing native from scratch — it's deliberately scoping the PWA and handing the rest to native.

In practice this hybrid approach usually runs in two stages. First, the whole product ships as a PWA: notifications, offline viewing, and basic forms stay in the web app, and App Store review never enters the picture. Second, if user data confirms a need the PWA can't meet (in-app purchase, deep platform integration), a native shell — a thin layer over WKWebView, or a separate native screen — is added for just that part. This ordering is far cheaper than writing native from day one and later realizing the PWA would've sufficed, since stage one's cost stays capped at a web project's cost.

GOLDEN TIP

The most valuable insight in this article

This tip holds the article's most important takeaway.

Easter Egg

You found a hidden gem!

There's a hidden detail in this section. Want to uncover it?

Easter Egg

You found a hidden gem!

There's a hidden detail in this section. Want to uncover it?

Reader Reward

If you've read this far, you now know most of the things people overlook when setting up web push on iOS. The short checklist below is for one last pass before you ship.

FAQ

Can push notifications be sent to a PWA on iOS?

Yes, since Safari 16.4 (March 27, 2023). The web app must first be added to the Home Screen, and then push permission must be requested via a direct user interaction. Notifications are delivered via APNs, and no Apple Developer Program membership is required for this.

What's the real difference between a PWA and a native app?

In terms of notifications, offline operation, and badging, the gap is now small — all of these exist in a web app too. The real difference is in deep hardware access (like Bluetooth or continuous background location tracking) and whether installation can be automatically nudged via the App Store: iOS has no beforeinstallprompt, so installation is always a manual Share menu step.

For which product is a PWA enough?

For notification-heavy, moderately complex products (news, e-commerce reminders, appointment notifications, content apps), a PWA is enough on iOS. For products that need deep hardware integration or continuous background sync, a service worker's scope falls short.

What happens if push permission is denied?

You can't automatically re-request a denied permission through the browser; the user has to open it back up themselves. WebKit states that web app notification permissions are managed per web app from Notification Settings — you need to show clear instructions directing the user there. That's why delaying the permission request until a moment the user sees value reduces this risk.

Do I need to write different server-side code for web push on iOS?

No. Apple's own documentation confirms that existing standard Push API code (VAPID, applicationServerKey, pushManager.subscribe) for Safari 16.0 and later works the same way as it does in other browsers. As long as you're not doing browser sniffing, you don't need to write an extra branch.

Can I show a banner that nudges users to install the PWA on iOS?

No, at least not in the sense of Chromium's beforeinstallprompt event — that event doesn't exist at all on iOS Safari. The only thing you can do is manually walk the user through the "Add to Home Screen" step from the Share menu using your own UI element (e.g., a fixed strip at the bottom of the page); you don't get an API call that triggers the install itself.

Update (September 2026)

This post was originally published on December 16, 2025. In the nine months since, WebKit hasn't announced any major change removing a limit around the push permission flow, background delivery, the Badging API, or the Add to Home Screen requirement. Safari 27.0's release notes (Web API, Networking, and Storage sections included) list no New Features item involving "push," "Notification," or "badge"; the sole Notification entry is a URL-parsing fix (176762955).

The one concrete development this cycle is for PWAs, but not on the push side: Safari 27.0 added the Service Worker Static Routing API. WebKit describes it as letting "a service worker define routing rules that let the browser skip invoking the service worker entirely for certain requests" — cutting overhead in high-performance PWAs by avoiding unnecessary service worker invocations. It's a performance improvement, not a new door in web push or notification capabilities.

In short: the core rules of web push on iOS (the Add to Home Screen requirement, permission requests limited to user interaction, a system that doesn't guarantee delivery) haven't changed since December 2025.

Conclusion

PWA on iOS is no longer "native, but missing pieces" — it's a distinct distribution option with clear boundaries. Web push, the Badging API, and Focus integration give notification-heavy products a genuine alternative, but it doesn't replace native for automatic install nudges, continuous background tasks, or hardware access. Draw a clear line for your product: notifications and basic interaction alone, a PWA is enough; deep platform integration required, add a thin native shell.

If you want to go deeper on related topics: the native APNs side of notifications is in Advanced Push Notifications, the in-app purchase decision is in the StoreKit 2 Production Guide, offline architecture is in iOS Offline-First Architecture, cross-platform deep linking is in Deep Linking and Universal Links, and the new material system is in iOS 26 Liquid Glass.

Sources

Tags

#PWA#web push#Safari#service worker#Badging API#iOS#web app
Muhittin Çamdalı

Muhittin Çamdalı

Lead Mobile Engineer

Lead Mobile Engineer with 12+ years of experience. Expert in iOS, Android and cross-platform architectures with Swift, SwiftUI, Kotlin and Flutter. I build performant, user-friendly mobile apps.

iOS Development News

Weekly Swift tips, SwiftUI tricks and iOS best practices. No spam, only valuable content.

We respect your privacy. You can unsubscribe at any time.

Share