Skip to content

Safari PWA Limitations on iOS: Why Progressive Web Apps Struggle on Apple Devices

Problem

When I build progressive web apps, Safari on iOS causes major headaches.

I built a PWA with push notifications, background sync, and offline storage. It worked great on Android Chrome. But on iOS Safari, notifications didn’t work. Background sync didn’t exist. Storage got cleared after 7 days.

My PWA was crippled on iOS.

Environment

  • iOS Safari 17+
  • Testing on iPhone (actual device)
  • Service Worker registered

What Happened

I had a standard PWA setup:

PWA setup
// Register service worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
// Subscribe to push notifications
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: vapidKey
});

On Android Chrome, this worked perfectly. On iOS Safari:

  • Push notifications failed
  • Background sync was unavailable
  • Storage got cleared unexpectedly

The iOS PWA Feature Gap

Here’s what I discovered:

PWA feature comparison
Feature | Chrome | Safari iOS
---------------------|--------|------------
Push Notifications | ✅ | ⚠️ iOS 16.4+
Background Sync | ✅ | ❌
Periodic Sync | ✅ | ❌
Storage Persistence | ✅ | ⚠️ Limited
File System Access | ✅ | ❌
Web Share API | ✅ | ✅
Install Prompt | ✅ Auto| ⚠️ Manual

Push Notifications: iOS 16.4+

iOS 16.4 added push notification support for installed PWAs. But:

  • Users must install the PWA first
  • Notifications only work when PWA is installed
  • Older iOS versions don’t support this at all

No Background Sync

Background sync allows offline actions to complete when connectivity returns. iOS Safari doesn’t support this.

Background sync (not on iOS)
// This works in Chrome, not Safari
self.registration.sync.register('sync-data');

Storage Gets Cleared

iOS Safari may clear website storage after 7 days of no use. This includes:

  • IndexedDB
  • LocalStorage
  • Service Worker cache

For a PWA, this is catastrophic. User data disappears.

Why This Happens

Apple’s Strategic Position

Apple controls both:

  • iOS App Store (30% revenue cut)
  • iOS Safari (WebKit engine)

PWAs bypass the App Store. There’s a conflict of interest.

Apple's conflict
PWA success → App Store revenue loss
WebKit PWA limitations → Protect App Store revenue

iOS Browser Engine Restriction

All iOS browsers must use WebKit. Even Chrome on iOS uses Safari’s rendering engine.

iOS browser reality
Chrome on iOS = Safari WebKit engine
Firefox on iOS = Safari WebKit engine
Edge on iOS = Safari WebKit engine
All iOS browsers share Safari's PWA limitations

This means PWA limitations affect ALL iOS browsers, not just Safari.

How to Work Around It

Solution #1: Feature Detection

Always detect capabilities before using them:

Feature detection for PWA
const pwaCapabilities = {
serviceWorker: 'serviceWorker' in navigator,
pushManager: 'PushManager' in window,
backgroundSync: 'SyncManager' in window,
persistentStorage: navigator.storage && 'persist' in navigator.storage
};
console.log('PWA Capabilities:', pwaCapabilities);

This tells me exactly what features I can use.

Solution #2: iOS Push Notification Handling

For push notifications, handle iOS specifically:

iOS push notification fallback
async function setupPushNotifications() {
if (!('PushManager' in window)) {
// iOS doesn't support push
// Show in-app notifications instead
setupInAppNotifications();
return;
}
// Check if we're on a supported iOS version
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
if (isIOS) {
// iOS 16.4+ supports push for installed PWAs
// Prompt user to install PWA first
const isStandalone = window.matchMedia('(display-mode: standalone)').matches
|| window.navigator.standalone;
if (!isStandalone) {
showIOSInstallPrompt();
return;
}
}
// Subscribe to push
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: vapidKey
});
return subscription;
}

Solution #3: Storage Persistence

Request persistent storage to reduce clearing risk:

Storage persistence
async function requestPersistentStorage() {
if (navigator.storage && navigator.storage.persist) {
const isPersisted = await navigator.storage.persist();
console.log(`Storage persisted: ${isPersisted}`);
return isPersisted;
}
return false;
}

Note: iOS may still clear storage, but this requests protection.

Solution #4: Server-Side Backup

For critical data, sync to server:

Server-side data backup
async function saveWithBackup(data) {
// Try local storage first
try {
localStorage.setItem('app-data', JSON.stringify(data));
} catch (e) {
console.log('Local storage failed');
}
// Always sync to server
await fetch('/api/sync', {
method: 'POST',
body: JSON.stringify(data)
});
}
async function loadData() {
// Try server first
const response = await fetch('/api/sync');
if (response.ok) {
return response.json();
}
// Fallback to local
const local = localStorage.getItem('app-data');
return local ? JSON.parse(local) : null;
}

Solution #5: iOS Install Detection

iOS doesn’t fire beforeinstallprompt. Detect and show custom UI:

iOS PWA install detection
function checkIOSInstall() {
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
const isStandalone = window.matchMedia('(display-mode: standalone)').matches
|| window.navigator.standalone;
if (isIOS && !isStandalone) {
// Show custom "Add to Home Screen" instructions
showIOSInstallBanner();
return false;
}
return true; // Already installed or not iOS
}
function showIOSInstallBanner() {
// Show overlay with instructions:
// 1. Tap Share button
// 2. Tap "Add to Home Screen"
// 3. Tap "Add"
}

EU Digital Markets Act Impact

The EU’s Digital Markets Act may force Apple to allow alternative browser engines on iOS. This could enable:

  • Chrome with Blink engine on iOS
  • Full PWA support through alternative browsers

But this is EU-only for now, and Apple is fighting implementation.

Summary

In this post, I explained Safari’s PWA limitations on iOS. The key points are:

  • iOS Safari lacks background sync, has limited storage, and push notifications only work in iOS 16.4+ for installed PWAs
  • All iOS browsers use WebKit, so Chrome/Firefox on iOS share the same limitations
  • Use feature detection, server-side backup, and iOS-specific install prompts to work around these gaps

For critical applications requiring full PWA features on iOS, a native app may still be necessary. But for many use cases, careful fallbacks can deliver a good experience.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments