Las notificaciones push para PWA en iOS son uno de los temas más comentados
entre los desarrolladores web en los últimos dos años. Apple mantuvo esta
función cerrada durante mucho tiempo, y cuando la abrió, lo hizo con
restricciones que aún generan dudas en la práctica.
En este artículo, un análisis técnico basado en mi experiencia con dos
proyectos reales: Kazki AI, un servicio de generación de cuentos infantiles
por prompt (proyecto ya cerrado), y el blog de un psicólogo practicante,
donde las notificaciones push informaban a los suscriptores sobre nuevos
cursos y publicaciones. Qué funciona, qué no, y qué código se necesita para
que todo funcione en iPhone.
Si aún estás eligiendo entre PWA y una aplicación nativa, lee primero
PWA vs Nativo en 2026: comparación y caso real
.
📚 Índice del artículo
🎯 Sección 1. Por qué iOS no admitió notificaciones push para PWA durante años
Respuesta corta: hasta iOS 16.4 (marzo de 2023), las aplicaciones web en iPhone
no podían recibir notificaciones push en absoluto; Apple simplemente no implementó
Web Push para iOS, aunque Android lo admitía desde hacía muchos años.
Android (Chrome) admite Web Push desde 2015. Apple añadió soporte
para iPhone y iPad solo en iOS 16.4, ocho años después. Oficialmente, Apple
no explicó las razones. La hipótesis más extendida en la industria es de negocios:
las PWA con notificaciones push completas reducen el incentivo para publicar aplicaciones en la App Store, donde Apple cobra una comisión del 15-30%. Pero esto es una suposición, no un hecho confirmado.
Hasta iOS 16.4, las aplicaciones web en iPhone no podían recibir
notificaciones push; Web Push simplemente no existía en la plataforma.
Técnicamente no era un obstáculo: Safari en macOS admitía
notificaciones push para sitios web desde OS X Mavericks (2013), utilizando
APNs (Apple Push Notification service). Sin embargo, era una implementación propia de Apple, y el estándar Web Push (Push API + VAPID) apareció
en Safari en macOS solo en la versión 16 (2022). Fue sobre esta base que Apple
construyó el soporte para iOS.
[WebKit: Web Push para aplicaciones web en iOS y iPadOS]
La presión externa también jugó un papel: la atención regulatoria a la App Store
en la UE y otras jurisdicciones cambió gradualmente la postura de Apple hacia las aplicaciones web.
Un episodio revelador ocurrió más tarde, en febrero de 2024: en la versión beta de iOS 17.4
para usuarios de la UE, Apple declaró que no podría admitir aplicaciones web de pantalla de inicio
en su forma habitual debido a los requisitos de la Ley de Mercados Digitales, y tras una ola de críticas de los desarrolladores, renunció a esta decisión
antes del lanzamiento. Es decir, Web Push para iOS apareció antes e independientemente de esta historia.
[Brainhub: PWA en iOS — Estado actual]
🎯 Sección 2. iOS 16.4, 17, 18: cronología de cambios — qué apareció y qué sigue roto
El cambio clave ocurrió una vez, en iOS 16.4. Las versiones posteriores
no cambiaron el modelo principal, y el problema de la "desaparición" de la suscripción,
según informes de desarrolladores, no ha desaparecido por completo.
iOS 16.4 abrió Web Push para PWA añadidas a la pantalla de inicio y permitió
a los navegadores de terceros ofrecer "Añadir a la pantalla de inicio". iOS 17 y 18
no añadieron cambios drásticos en Web Push, pero los desarrolladores siguen
registrando casos en los que la suscripción push "desaparece" sin una razón aparente.
iOS 16.4 es el comienzo, no el final. El modelo principal (push solo
para PWA instaladas) no ha cambiado, y la estabilidad de entrega
en iOS, según la experiencia de los desarrolladores, todavía es inferior a la de Android.
iOS 16.4 (marzo de 2023) — la apertura
El primer lanzamiento con soporte para Web Push para aplicaciones web de pantalla de inicio.
Las notificaciones aparecen en la pantalla de bloqueo, en el Centro de Notificaciones
y en el Apple Watch. También se añadió soporte para Badging API.
En el mismo lanzamiento, los navegadores de terceros (Chrome, Edge y otros) obtuvieron
la capacidad de ofrecer a los usuarios "Añadir a la pantalla de inicio" en el menú Compartir.
[WebKit: Web Push para aplicaciones web en iOS y iPadOS]
La práctica mostró un problema: los desarrolladores informaron que las suscripciones push dejaban de funcionar después de 1-2 semanas. El endpoint se volvía inactivo y el usuario tenía que reinstalar la PWA para volver a suscribirse.
[Foros de desarrolladores de Apple: Problemas de Push PWA en iOS]
iOS 17 (septiembre de 2023) — sin cambios en Web Push
En iOS 17, el modelo principal de Web Push no cambió: la suscripción, como antes,
solo es posible para la aplicación web añadida a la pantalla de inicio. Algunos desarrolladores
notaron mejoras en la estabilidad, pero las quejas sobre la desaparición de suscripciones
persistieron.
[OneSignal: Configuración de Web Push para iOS]
iOS 17.4 (febrero-marzo de 2024) — el episodio de la UE
En la versión beta de iOS 17.4 para usuarios de la UE, Apple anunció el abandono
de las aplicaciones web de pantalla de inicio completas (se abrirían como pestañas normales de Safari). Tras las críticas de desarrolladores y reguladores, esta decisión
se revocó y se mantuvo el soporte para PWA en la UE.
iOS 18 (septiembre de 2024) — estabilización
No hubo cambios significativos en la API de Web Push. Algunos desarrolladores registran
mejoras en la fiabilidad de la entrega, otros — los mismos problemas con la pérdida
de suscripciones después de un largo período de inactividad de la aplicación.
[Foros de desarrolladores de Apple: discusión sobre iOS 17-18]
🎯 Sección 3. Condición principal: la PWA debe estar añadida a la pantalla de inicio — sin importar a través de qué navegador
Las notificaciones push en iOS solo funcionan para la aplicación web añadida
a la pantalla de inicio (Compartir → Añadir a la pantalla de inicio). Una pestaña abierta —
ya sea en Safari o en cualquier otro navegador — no permite la suscripción a push.
A diferencia de Android, donde Chrome puede solicitar permiso para push
directamente en la pestaña, iOS requiere que el sitio esté instalado como una aplicación separada
en la pantalla de inicio. A partir de iOS 16.4, se puede añadir no solo a través de Safari: los navegadores de terceros también pueden ofrecer
"Añadir a la pantalla de inicio" en el menú Compartir. El resultado es el mismo: el sitio se abre
como una aplicación web de pantalla de inicio y tiene el mismo soporte para push. Importante: la presencia
del elemento en el menú depende del navegador específico, por lo que en las instrucciones
para el usuario es mejor mostrar la ruta universal
"Compartir → En pantalla de inicio".
Web Push en iOS solo está disponible para aplicaciones web de pantalla de inicio:
un sitio añadido a la pantalla de inicio puede solicitar permiso para notificaciones,
pero solo en respuesta a una acción del usuario, por ejemplo, al hacer clic en el botón
"Suscribirse".
Esto significa que antes de solicitar permiso para notificaciones,
es necesario verificar tres cosas: si es iOS, si está disponible
PushManager, y si la aplicación está abierta en modo independiente
(es decir, instalada en la pantalla de inicio). Tenga en cuenta: iPad con iPadOS 13+
se presenta en el user agent como Mac, por lo que las comprobaciones solo en
iphone|ipad no son suficientes.
function canUsePush() {
const isIOS =
/iphone|ipad|ipod/i.test(navigator.userAgent) ||
(navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1); // iPadOS
const isStandalone =
window.matchMedia('(display-mode: standalone)').matches ||
window.navigator.standalone === true;
if (isIOS && !isStandalone) {
// Mostrar indicación: "Añade a la pantalla de inicio para recibir notificaciones"
showInstallPrompt();
return false;
}
return 'serviceWorker' in navigator && 'PushManager' in window;
}
Si se omite esta verificación, en una pestaña normal en iOS las API para push
no estarán disponibles (por ejemplo, PushManager estará ausente), y el código
de suscripción fallará con un error. El usuario no entenderá por qué el botón
"suscribirse" no funciona, por lo que la indicación de instalación debe
mostrarse con antelación.
🎯 Раздел 4. Пошагово: подписка пользователя на Push на iOS
Подписка на iOS требует user gesture —
запрос разрешения нельзя вызывать автоматически при загрузке страницы,
только в ответ на действие пользователя.
На iOS веб-приложение, добавленное на домашний экран, может запросить разрешение
на уведомления только в ответ на прямое взаимодействие — например, нажатие
кнопки "Подписаться". Поэтому Notification.requestPermission()
нужно вызывать непосредственно из обработчика клика. Вызовы из
setTimeout, DOMContentLoaded или другого
автоматического кода на iOS не сработают. На Android такой запрос технически
возможен и без клика, но браузеры всё чаще блокируют его,
поэтому кнопка-триггер — универсальная практика.
[WebKit: Web Push for Web Apps on iOS and iPadOS]
Запрос разрешения на push на iOS принимается только в ответ на действие
пользователя: автоматические запросы при загрузке страницы не работают.
[Apple WWDC22: Meet Web Push for Safari]
Вот полный flow подписки из реального проекта Kazki AI
(функцию canUsePush() мы определили в предыдущем разделе):
const VAPID_PUBLIC_KEY = 'YOUR_PUBLIC_VAPID_KEY';
// Конвертация VAPID-ключа (base64url) в Uint8Array для subscribe()
function urlBase64ToUint8Array(base64String) {
const padding = '='.repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');
const raw = atob(base64);
return Uint8Array.from([...raw].map(ch => ch.charCodeAt(0)));
}
async function initPushSubscription() {
// 1. Проверка поддержки и standalone-режима на iOS (см. раздел 3)
if (!canUsePush()) return;
try {
// 2. Запрос разрешения — ПЕРВЫМ асинхронным действием в обработчике клика,
// пока user gesture ещё "жив". Если разрешение уже предоставлено —
// метод сразу вернёт 'granted'.
const permission = await Notification.requestPermission();
if (permission !== 'granted') return; // 'denied' — показать подсказку о настройках
// 3. Ожидаем Service Worker
const reg = await navigator.serviceWorker.ready;
// 4. Берём существующую подписку или создаём новую
let subscription = await reg.pushManager.getSubscription();
if (!subscription) {
subscription = await reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
}
// 5. Сохраняем (или обновляем) подписку на сервере
const { endpoint, keys } = subscription.toJSON();
const response = await fetch('/api/push/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
endpoint: endpoint,
p256dh: keys.p256dh,
auth: keys.auth
})
});
if (!response.ok) throw new Error('Сервер отклонил подписку: ' + response.status);
} catch (e) {
console.error('Push ошибка:', e.message);
}
}
// Подписываем только по клику — не автоматически!
document.querySelector('#push-btn')
.addEventListener('click', initPushSubscription);
Обратите внимание на порядок: requestPermission() вызывается
до любых await на другие операции. Кроме того, подписку
лучше отправлять на сервер каждый раз, даже если она уже существует — так
бэкенд всегда имеет актуальный endpoint.
Эндпоинт подписки на iOS, как правило, начинается с
https://web.push.apple.com/, в Chrome на Android — с
https://fcm.googleapis.com/. Это удобно для диагностики,
но не стоит жёстко привязываться к этому логике: формат может измениться.
[pqvst.com: Demystifying Web Push Notifications]
🎯 Раздел 5. Web App Manifest и Service Worker: обязательные настройки
Для Web Push на iOS сайт должен быть установлен как Home Screen web app,
а манифест — объявлять режим отображения standalone
(или fullscreen). Без этого Push API в вашем приложении
не заработает, даже если всё остальное настроено правильно.
Apple рассматривает как "Home Screen web app" сайт, чей манифест задаёт
режим отображения, отличный от обычной вкладки. Если этого нет,
PushManager недоступен, и код подписки не выполнится.
Практический вывод: всегда указывайте "display": "standalone".
[GitHub: webpush-ios-example]
Push на iOS доступен только для сайтов, добавленных на домашний экран, —
а манифест с display: standalone является практическим
условием для корректной работы такого приложения.
Минимальный manifest.json для работы Push на iOS:
{
"name": "Kazki AI",
"short_name": "Kazki",
"display": "standalone",
"start_url": "/",
"scope": "/",
"background_color": "#ffffff",
"theme_color": "#6366f1",
"icons": [
{
"src": "/images/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/images/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Иконки должны быть реальными, доступными PNG-файлами корректного размера.
Дополнительно подключите <link rel="apple-touch-icon" href="/images/icon-192.png">
в <head>: iOS традиционно ориентируется именно на него.
Если иконки нет, iOS подставит запасную (fallback) иконку, и приложение
будет выглядеть непрезентабельно на домашнем экране.
Service Worker: почему нельзя кешировать авторизацию и API
Типичная ошибка — агрессивное кеширование, которое ломает авторизацию.
Вот подход из Kazki AI: кешируем только статические файлы с вашего домена
(стили, скрипты, изображения, шрифты), а HTML-страницы, API и
авторизацию не трогаем вообще:
const CACHE_NAME = 'static-v1';
const STATIC_DESTINATIONS = ['style', 'script', 'image', 'font'];
self.addEventListener('fetch', event => {
const request = event.request;
const url = new URL(request.url);
// Не кешируем: не-GET, чужие домены, API
if (request.method !== 'GET') return;
if (url.origin !== self.location.origin) return;
if (url.pathname.startsWith('/api/')) return;
// HTML-страницы (login, dashboard, profile и т.д.) всегда идут из сети —
// кешируем только статические ресурсы
if (!STATIC_DESTINATIONS.includes(request.destination)) return;
// Network-first, fallback на кеш
event.respondWith(
fetch(request)
.then(response => {
if (response.ok) {
const clone = response.clone();
caches.open(CACHE_NAME)
.then(cache => cache.put(request, clone));
}
return response;
})
.catch(() => caches.match(request))
);
});
Если закешировать страницу авторизации — пользователь увидит устаревшую
версию или "застрянет" в кешированном состоянии после выхода из аккаунта.
Компромисс этого подхода: офлайн-режим для HTML-страниц не работает.
Если он вам нужен, добавьте отдельную офлайн-страницу-заглушку.
Подробнее о типичных ошибках кеширования в PWA — включая агрессивное
кеширование HTML и проблемы роутинга — читайте в статье
8 критических ошибок при интеграции PWA
.
💼 Раздел 6. Push handler: try/catch fallback и iOS-специфика из реального проекта
Каждое push-событие в Service Worker должно заканчиваться показом
уведомления, а payload нужно разбирать безопасно: если данные пришли
не в JSON-формате, код без try/catch упадёт, уведомление не будет показано,
и iOS может отменить подписку.
iOS не поддерживает "тихие" пуши: если Service Worker получает push-событие
и не показывает уведомление, Safari может отозвать подписку. По сообщениям
разработчиков, это происходит уже после нескольких таких случаев. Поэтому
event.waitUntil с showNotification и корректный
fallback для payload критически важны.
[Apple Developer: Sending Web Push Notifications]
Если Service Worker не отображает уведомление после push-события —
iOS расценивает это как "тихий пуш" и может отменить подписку.
[dev.to: iOS push subscriptions terminated after 3 notifications]
Полный push handler из Kazki AI с пояснениями:
self.addEventListener('push', event => {
// try/catch — payload может быть не JSON (ошибка на сервере, тестовый пуш)
let data = {};
try {
data = event.data?.json() ?? {};
} catch (e) {
data = { title: 'Kazki AI', body: event.data?.text() ?? '' };
}
// Badging API — счётчик на иконке. Ошибка бейджа не должна
// сломать показ уведомления, поэтому глушим её через catch.
const badgePromise = self.navigator?.setAppBadge
? self.navigator.setAppBadge(data.badgeCount || 1).catch(() => {})
: Promise.resolve();
// event.waitUntil + showNotification — ОБЯЗАТЕЛЬНО для каждого push-события,
// иначе iOS может отменить подписку
event.waitUntil(
Promise.all([
self.registration.showNotification(data.title || 'Kazki AI', {
body: data.body || '',
icon: data.icon || '/images/favicon.jpg',
badge: '/images/favicon.jpg',
data: { url: data.url || '/dashboard' }
}),
badgePromise
])
);
});
// Клик по уведомлению — открываем нужную страницу
self.addEventListener('notificationclick', event => {
event.notification.close();
const targetUrl = new URL(
event.notification.data?.url || '/dashboard',
self.location.origin
).href;
event.waitUntil((async () => {
if (self.navigator?.clearAppBadge) {
await self.navigator.clearAppBadge().catch(() => {});
}
// Если приложение уже открыто — переходим в него, а не открываем новое окно
const windowClients = await clients.matchAll({ type: 'window', includeUncontrolled: true });
for (const client of windowClients) {
if ('focus' in client) {
await client.focus();
if ('navigate' in client) await client.navigate(targetUrl);
return;
}
}
await clients.openWindow(targetUrl);
})());
});
Структура payload, который отправляет бэкенд (Spring Boot + nl.martijndwars/web-push):
{
"title": "Новая сказка готова!",
"body": "Твоя сказка про дракона уже ждёт",
"icon": "/images/favicon.jpg",
"url": "/library",
"badgeCount": 1
}
Поле badgeCount должно содержать актуальное количество непрочитанных
уведомлений, которое считает сервер: Service Worker не знает предыдущего значения.
💼 Раздел 7. 7 причин почему пуши не приходят на iPhone — и как это исправить
Большинство проблем с iOS PWA Push
связаны не с сервером, а с тем, как клиентский код взаимодействует
с ограничениями Safari.
Проблемы с push-подписками на iOS — одна из наиболее обсуждаемых
тем на Apple Developer Forums. Ниже — семь самых распространённых причин
с конкретными решениями.
[Apple Developer Forums: PWA Push Issues]
1. PWA не установлено на домашний экран
Самая распространённая причина. Push API недоступен, пока приложение
не открыто как Home Screen web app (standalone-режим). Решение:
добавить UI-подсказку с инструкцией "Поделиться → На экран Домой".
2. Service Worker не отображает уведомление после push-события
iOS расценивает пуш без showNotification как "тихий"
и может отменить подписку после нескольких таких случаев. Решение:
всегда вызывать showNotification внутри
event.waitUntil, даже если payload пустой или некорректный.
[dev.to: iOS push terminated after 3 notifications]
3. VAPID subject в неправильном формате
Subject в VAPID должен быть mailto: адресом или
HTTPS URL. Если он некорректен, Apple может отклонить запрос
(например, со статусом 403 Forbidden от
web.push.apple.com). Решение: проверить
vapid.subject в конфигурации сервера.
[Longsight: Implementing Cross-Platform Push]
4. Подписка "пропала" после повторной авторизации
Если фронтенд вызывает subscription.unsubscribe()
во время logout, повторно подписаться тихо уже не получится: на iOS новый
subscribe() требует нового user gesture, то есть пользователю
снова придётся нажать кнопку. По опыту разработчиков, надёжнее всего
не отменять подписку при logout, а лишь помечать её неактивной
на сервере и связывать с аккаунтом после следующего входа.
[XenForo: Lost push subscriptions iOS PWA]
5. HTTP вместо HTTPS
Service Worker и Push API требуют безопасного контекста, то есть HTTPS
(единственное исключение — localhost во время разработки; на iPhone
для тестирования нужен реальный HTTPS-домен). Отдельный подводный камень:
файл Service Worker нельзя отдавать через редирект — регистрация
в таком случае завершится ошибкой. Решение: проверить, что весь сайт,
включая статику и файл SW, обслуживается через HTTPS без редиректов
на сам скрипт.
6. Запрос разрешения вызывается не из user gesture
Если Notification.requestPermission() вызывается в
setTimeout, DOMContentLoaded или
любом автоматическом коде — на iOS запрос не сработает.
Решение: вызывать исключительно в обработчике клика на кнопку.
[pwa.io: Web Push iOS Safari 16.4]
7. Endpoint вернул 410 или 404 — подписка не удалена из БД
Когда подписка становится недействительной, push-сервис возвращает статус
410 Gone или 404. Если не удалить
этот endpoint из базы — последующие попытки отправить пуш этому
пользователю будут тратить ресурсы. Решение: обрабатывать именно эти
статус-коды и удалять подписку автоматически. Другие ошибки
(400, 401, 403, 413)
обычно означают проблему в вашем запросе — VAPID, payload или его
размер, — поэтому подписку по ним удалять нельзя: можно потерять
живых подписчиков.
// Spring Boot: обработка невалидных подписок
if (statusCode == 410 || statusCode == 404) {
pushSubscriptionRepository.deleteByEndpoint(sub.getEndpoint());
log.info("Удалена невалидная подписка: {}", sub.getEndpoint());
} else if (statusCode >= 400) {
// Не удаляем: вероятно, проблема в VAPID, payload или конфигурации
log.warn("Push не доставлено, статус {} для {}", statusCode, sub.getEndpoint());
}
💼 Раздел 8. Обходные пути и стратегии отката
Если PWA Push на iOS не подходит для вашего случая — email и in-app уведомления остаются более надежной альтернативой, так как не зависят от установки PWA и состояния push-подписки.
PWA Push на iOS работает, но имеет ограничения: требуется установка на домашний экран, возможна потеря подписок, а стабильность доставки, по опыту разработчиков, ниже, чем на Android и в нативных приложениях. Для критических уведомлений стоит иметь стратегию отката.
Надежная система уведомлений — это не выбор между Push и email, а комбинация каналов с разумной деградацией.
Email как основной вариант отката
Email не зависит от версии iOS или статуса установки PWA, а доставку можно отслеживать (bounce, открытия, жалобы). Для транзакционных уведомлений (оплата, подтверждение, важные события) это более надежный канал. Ограничение: он работает только для пользователей, чей адрес у вас есть, а доставка зависит от репутации вашего домена (SPF, DKIM, DMARC).
In-app уведомления
Счетчик непрочитанных в заголовке, лента событий внутри приложения — это то, что пользователь гарантированно увидит при следующем открытии PWA, независимо от статуса push-подписки.
Комбинированный подход с Kazki AI
В Kazki AI используется многоуровневая система:
- Push-уведомления — для пользователей, установивших PWA и разрешивших уведомления
- Badge на иконке (Badging API) — для установленных PWA, как дополнение к push
- In-app лента событий — для всех, кто открывает приложение
- Email — для всех пользователей как вариант отката и основной канал для транзакционных сообщений
Такая комбинация значительно расширяет охват: пользователь, не установивший PWA или не разрешивший push, все равно получит важную информацию через email и in-app. Гарантировать 100% охват не может ни один канал, но чем больше независимых каналов, тем меньше вероятность, что уведомление будет утеряно.
Подробнее о выборе между PWA и нативным приложением —
PWA vs Native в 2026: сравнение и реальный кейс
.
❓ Частые вопросы (FAQ)
Работает ли Web Push на iOS без установки PWA?
Нет. Push API на iOS доступен только для Home Screen web apps — то есть сайтов, добавленных на домашний экран через меню Share ("Поделиться → На экран Домой"). Открытая вкладка в Safari или любом другом браузере не дает возможности подписаться на push.
[WebKit Blog: Web Push for Web Apps on iOS]
Работает ли PWA Push через Chrome на iPhone?
Да, но с уточнением. Подписаться на push из вкладки Chrome невозможно, как и из вкладки Safari. Однако с iOS 16.4 сторонние браузеры могут предлагать "Add to Home Screen" в меню Share, и сайт, добавленный таким образом, открывается как Home Screen web app и имеет ту же поддержку push. Наличие этого пункта зависит от конкретного браузера, поэтому в инструкции для пользователей надежнее указывать путь "Поделиться → На экран Домой". Также стоит знать, что на iOS браузеры по умолчанию используют WebKit; в ЕС, начиная с iOS 17.4, Apple допускает и альтернативные движки.
[ WebKit Blog: Web Push for Web Apps on iOS]
С какой версии iOS поддерживается Web Push для PWA?
С iOS 16.4 (март 2023). В версиях до 16.4 Web Push для PWA недоступен независимо от браузера или настроек. Поскольку большинство активных iPhone давно обновлены выше 16.4, версия iOS сегодня редко становится причиной проблем; точную долю на вашей аудитории можно посмотреть в собственной аналитике.
[ WebKit Blog: Web Push iOS 16.4]
Почему подписка исчезает через несколько дней?
Есть несколько причин, о которых сообщают разработчики. Первая — iOS может отменить подписку, если Service Worker получал push-события, но не отображал уведомления. Вторая — после длительной неактивности приложения подписка может стать недействительной (сервер получит 410 или 404). Решение: всегда вызывать showNotification в event.waitUntil, проверять актуальность подписки при каждом открытии приложения и повторно отправлять ее на сервер.
[dev.to: iOS push subscriptions terminated]
Какой delivery rate у PWA Push на iOS по сравнению с Android?
Универсальной цифры нет: независимых публичных данных мало, а результат зависит от вашей аудитории и доли "мертвых" подписок. По опыту разработчиков, на iOS доставка стабильно хуже, чем на Android: более строгие правила Safari относительно уведомлений и подписок, становящихся недействительными после неактивности. Надежнее всего — измерить собственный показатель: сравните количество успешных отправок (ответы 2xx) с общим количеством подписок и отдельно посчитайте, сколько подписок удаляется через 410/404.
[OneSignal: iOS Web Push]
Можно ли отправить пуш без библиотеки на бэкенде?
Технически да — Web Push Protocol является открытым стандартом. Но самостоятельная реализация VAPID-подписи и шифрования payload сложна и подвержена ошибкам. Для Java/Spring Boot можно использовать библиотеку nl.martijndwars:web-push (ей также нужен провайдер BouncyCastle: перед отправкой зарегистрируйте его через Security.addProvider(new BouncyCastleProvider())), для Node.js — web-push.
<!-- Maven: зависимость для Java/Spring Boot.
Актуальную версию проверьте на Maven Central -->
<dependency>
<groupId>nl.martijndwars</groupId>
<artifactId>web-push</artifactId>
<version>5.1.1</version>
</dependency>
Как сгенерировать VAPID ключи?
Самый простой способ — через web-push CLI:
npx web-push generate-vapid-keys
Публичный ключ используется на фронтенде при подписке, приватный — на бэкенде для подписи каждого push-запроса. Никогда не публикуйте приватный ключ в открытом репозитории. Генерируйте пару один раз и сохраняйте ее: существующие подписки привязаны к публичному ключу, поэтому после его замены пользователям придется подписываться заново.
✅ Выводы: когда PWA Push на iOS достаточно, а когда лучше натив
PWA Push на iOS работает и подходит для большинства контентных и SaaS-продуктов. Для критических уведомлений в финтехе, медицине или e-commerce с большой долей iOS-аудитории стоит либо комбинировать с email, либо рассматривать натив.
С iOS 16.4 технический барьер снят. Остается операционный: пользователь должен добавить PWA на домашний экран и разрешить уведомления — и это главное ограничение, которое не решается на уровне кода.
PWA Push на iOS — это не "либо работает, либо нет". Это инструмент с конкретными условиями работы. Если эти условия выполнены — он работает достаточно надежно для информационных уведомлений.
PWA Push на iOS подходит, если:
- Ваш продукт — контентный: новости, медиа, образование, SaaS
- Аудитория лояльна и готова установить PWA на домашний экран
- Уведомления информационные, не критичные (напоминания, новости, обновления)
- Есть email как вариант отката для тех, кто не установил PWA
- Бюджет не позволяет параллельно разрабатывать нативное приложение
Стоит рассмотреть натив или гибрид, если:
- Уведомления критические: банковские транзакции, медицинские аллерты
- Доля iOS в вашей аудитории высока (ориентир — более половины пользователей)
- Требуются фоновые процессы или геолокация в реальном времени
- App Store presence является частью маркетинговой стратегии
Вывод из опыта Kazki AI
В Kazki AI PWA Push на iOS работает удовлетворительно для информационных уведомлений — "новая сказка готова", "напоминание о подписке". Доставка на iOS менее стабильна, чем на Android, но приемлема для продукта, где критичность доставки невысока. Email остается основным каналом для транзакционных уведомлений.
Если вы еще выбираете между PWA и нативным приложением и не определились с архитектурой — подробное сравнение с реальными цифрами в статье
PWA vs Native в 2026: сравнение и реальный кейс
.
📖 Источники