PWA Push-сповіщення на iOS у 2026: що реально працює

Оновлено:
✦ Запитати AI про цю статтю
PWA Push-сповіщення на iOS у 2026: що реально працює

Push-сповіщення для PWA на iOS — одна з найбільш обговорюваних тем серед веб-розробників останніх двох років. Apple довго тримала цю функцію закритою, а коли відкрила — зробила це з обмеженнями, які досі викликають питання на практиці.

У цій статті — технічний розбір на основі мого досвіду з двома реальними проєктами: Kazki AI — сервіс генерації дитячих казок за промптом (проєкт уже закрито) та блог практикуючого психолога, де push-сповіщення повідомляли підписників про нові курси й публікації. Що працює, що ні, і який код потрібен щоб все запрацювало на iPhone.

Якщо ви ще вибираєте між PWA та нейтив-додатком — спочатку прочитайте PWA vs Native у 2026: порівняння та реальний кейс .

📚 Зміст статті

🎯 Розділ 1. Чому iOS роками не підтримував Push-сповіщення для PWA

Коротка відповідь: до iOS 16.4 (березень 2023) веб-додатки на iPhone не могли отримувати push-сповіщення взагалі — Apple просто не реалізувала Web Push для iOS, хоча Android підтримував його вже багато років.

Android (Chrome) підтримує Web Push з 2015 року. Apple додала підтримку для iPhone та iPad лише в iOS 16.4 — через вісім років. Офіційно Apple не пояснювала причин. Найпоширеніша гіпотеза в індустрії — бізнесова: PWA з повноцінними пушами зменшує стимул публікувати додатки в App Store, де Apple стягує комісію 15–30%. Але це припущення, а не підтверджений факт.

До iOS 16.4 веб-додатки на iPhone не могли отримувати push-сповіщення — Web Push просто був відсутній на платформі.

Технічною перешкодою це не було: Safari на macOS підтримував push-сповіщення для сайтів ще з OS X Mavericks (2013), використовуючи APNs (Apple Push Notification service). Щоправда, це була власна реалізація Apple, а стандартний Web Push (Push API + VAPID) з'явився в Safari на macOS лише у версії 16 (2022). Саме на цій базі Apple і побудувала підтримку для iOS. [WebKit: Web Push for Web Apps on iOS and iPadOS]

Зовнішній тиск також відіграв роль: регуляторна увага до App Store у ЄС та інших юрисдикціях поступово змінювала позицію Apple щодо веб-додатків. Показовий епізод стався пізніше, у лютому 2024 року: у бета-версії iOS 17.4 для користувачів ЄС Apple заявила, що не зможе підтримувати Home Screen web apps у звичному вигляді через вимоги Digital Markets Act, — і після хвилі критики від розробників відмовилась від цього рішення ще до релізу. Тобто Web Push для iOS з'явився раніше й незалежно від цієї історії. [Brainhub: PWA on iOS — Current Status]

🎯 Розділ 2. iOS 16.4, 17, 18: хронологія змін — що з'явилось і що досі зламано

Ключова зміна сталася один раз — в iOS 16.4. Наступні версії не змінювали принципової моделі, а проблема з "зникненням" підписки, за повідомленнями розробників, повністю не зникла.

iOS 16.4 відкрив Web Push для PWA, доданих на домашній екран, і дозволив стороннім браузерам пропонувати "Додати на екран Домівка". iOS 17 і 18 не додали кардинальних змін у Web Push, але розробники й надалі фіксують випадки, коли push-підписка "зникає" без очевидної причини.

iOS 16.4 — це старт, а не фінал. Принципова модель (push лише для встановлених PWA) не змінилась, а стабільність доставки на iOS, за досвідом розробників, досі поступається Android.

iOS 16.4 (березень 2023) — відкриття

Перший реліз з підтримкою Web Push для Home Screen web apps. Сповіщення з'являються на Lock Screen, у Notification Center та на Apple Watch. Також додана підтримка Badging API. У цьому ж релізі сторонні браузери (Chrome, Edge та інші) отримали можливість пропонувати користувачам "Add to Home Screen" у меню Share. [WebKit: Web Push for Web Apps on iOS and iPadOS]

Практика показала проблему: розробники повідомляли, що push-підписки через 1–2 тижні переставали працювати. Ендпоінт ставав неактивним, і користувач мав переустановити PWA щоб підписатись знову. [Apple Developer Forums: PWA Push Issues iOS]

iOS 17 (вересень 2023) — без змін у Web Push

У iOS 17 принципова модель Web Push не змінилась: підписка, як і раніше, можлива лише для веб-додатку, доданого на домашній екран. Частина розробників відзначала покращення стабільності, але скарги на зникнення підписок залишились. [OneSignal: iOS Web Push Setup]

iOS 17.4 (лютий–березень 2024) — епізод із ЄС

У бета-версії iOS 17.4 для користувачів ЄС Apple анонсувала відмову від повноцінних Home Screen web apps (вони відкривались би як звичайні вкладки Safari). Після критики розробників і регуляторів це рішення скасували, і підтримку PWA в ЄС залишили.

iOS 18 (вересень 2024) — стабілізація

Суттєвих змін у Web Push API не було. Частина розробників фіксує покращення надійності доставки, інші — ті самі проблеми з втратою підписок після тривалої неактивності додатку. [Apple Developer Forums: обговорення iOS 17–18]

🎯 Розділ 3. Головна умова: PWA має бути додана на домашній екран — не важливо, через який браузер

Push-сповіщення на iOS працюють лише для веб-додатку, доданого на домашній екран (Share → Add to Home Screen). Відкрита вкладка — у Safari чи в будь-якому іншому браузері — підписку на push не дає.

На відміну від Android, де Chrome може запросити дозвіл на пуші прямо у вкладці, iOS вимагає щоб сайт був встановлений як окремий додаток на домашньому екрані. Починаючи з iOS 16.4, додати його можна не лише через Safari: сторонні браузери теж можуть пропонувати "Add to Home Screen" у меню Share. Результат однаковий — сайт відкривається як Home Screen web app і має ту саму підтримку push. Важливо: наявність пункту в меню залежить від конкретного браузера, тому в інструкції для користувача краще показувати універсальний шлях "Поділитися → На екран Домівка".

Web Push на iOS доступний лише для Home Screen web apps: сайт, доданий на домашній екран, може запросити дозвіл на сповіщення, але лише у відповідь на дію користувача — наприклад, натискання кнопки "Підписатись".

Це означає що перед тим як запитувати дозвіл на сповіщення, потрібно перевірити три речі: чи це iOS, чи доступний PushManager, і чи додаток відкритий у standalone-режимі (тобто встановлений на домашній екран). Зверніть увагу: iPad з iPadOS 13+ у user agent представляється як Mac, тому перевірки лише на iphone|ipad недостатньо.


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) {
    // Показати підказку: "Додай на домашній екран щоб отримувати сповіщення"
    showInstallPrompt();
    return false;
  }

  return 'serviceWorker' in navigator && 'PushManager' in window;
}

Якщо пропустити цю перевірку, у звичайній вкладці на iOS API для push недоступні (наприклад, PushManager відсутній), і код підписки впаде з помилкою. Користувач не зрозуміє чому кнопка "підписатись" не працює — тому підказку про встановлення потрібно показувати завчасно.

PWA Push-сповіщення на iOS у 2026: що реально працює — 2

🎯 Розділ 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());
}

🧩 Готові сервіси замість власного бекенду: коли це має сенс

Усе, що описано вище, я реалізовував самостійно: VAPID-ключі, збереження підписок і відправку через бібліотеку на Spring Boot. Багато проблем зі списку з сімох причин (невалідні підписки, VAPID, обробка статусів) лежать саме на бекенді, тому природне питання: чи не передати цю частину готовому сервісу. Нижче короткий огляд п'яти популярних варіантів. Я їх у продакшені самостійно не підключав, тож порівняння базується на їхній офіційній документації, а не на власному досвіді. Докладний розбір буде в окремій статті: Push-сервіси для PWA: OneSignal, FCM, Pushwoosh, WonderPush і Braze.

Чого сервіс не змінює

Головне: готовий сервіс не знімає обмежень iOS. Документація всіх перевірених сервісів описує ті самі умови, що й у цій статті:

  • Для FCM на iOS і iPadOS застосунок потрібно додати на домашній екран.
  • Braze зазначає, що запросити дозвіл на push у Safari на iOS можна лише після додавання сайту на домашній екран і лише у відповідь на дію користувача.
  • OneSignal вимагає iOS 16.4 або новішої, додавання на домашній екран і відкриття застосунку звідти, а також прямо попереджає, що в частини розробників iOS web push спочатку працює, а потім раптово зупиняється.

Тобто сервіс прибирає частину роботи на бекенді, але установка на домашній екран, запит дозволу по кліку й нестабільність підписок на iOS залишаються.

Порівняння сервісів

Сервіс Тип iOS web push (за документацією) Що врахувати
Firebase Cloud Messaging (FCM) Інфраструктура доставки від Google Так. Safari на iOS і iPadOS 16.4+, застосунок має бути на домашньому екрані (Firebase Blog) Це ближче до власного бекенду: сервіс доставляє повідомлення, а логіку кампаній і сегментації ви будуєте самі. Можливості консолі перевірте в документації Firebase
OneSignal Платформа push-розсилок (SaaS) Так. iOS 16.4+, будь-який браузер із пунктом «На екран Домівка» (Safari, Chrome, Edge). Service Worker OneSignal обов'язковий (документація) Свій Service Worker (кешування, push handler) доведеться поєднувати з воркером сервісу. Є API для створення повідомлень
Pushwoosh Платформа push-розсилок (SaaS) [ документації Pushwoosh] []
WonderPush Платформа push-розсилок (SaaS) Так. Сумісність з iOS 16.4 заявлено ще в день релізу, 24 березня 2023 (блог WonderPush) Потрібен manifest з "display": "standalone": якщо власного немає, сервіс додає свій. За документацією, iOS-сповіщення текстові, без картинок і кнопок (сторінка давно не оновлювалась, перевірте)
Braze Платформа взаємодії з клієнтами (CEP) корпоративного рівня Так. Потрібні manifest, Service Worker із бібліотекою Braze і додавання на домашній екран (документація) Рішення для великих команд і мультиканальних кампаній. Умови використання й ціни перевіряйте на офіційному сайті. Якщо користувач відхилив запит на дозвіл, показати його знову можна лише після видалення та повторного додавання застосунку на домашній екран

Цін у таблиці свідомо немає: вони змінюються, тож перевіряйте їх на офіційних сторінках кожного сервісу.

Коли варто взяти сервіс, а коли залишити власний бекенд

Сервіс має сенс, якщо:

  • немає часу чи ресурсів підтримувати власну відправку й очищення підписок;
  • потрібні сегментація, аналітика й кампанії, які команда маркетингу веде без розробників;
  • сповіщення треба надсилати одночасно з вебу та з нативних застосунків.

Власний бекенд залишається розумним вибором, якщо:

  • сценарій простий, як у Kazki AI («нова казка готова»);
  • важливо, щоб дані підписників не залишали вашу інфраструктуру;
  • ви хочете мінімум залежностей і повний контроль над логікою відправки.

Що перевірити перед вибором

  • Частка iOS-аудиторії. Подивіться у власній аналітиці, скільки користувачів справді відкривають сайт з iPhone, бо саме для них діють усі обмеження цієї статті.
  • Конфлікт Service Worker. SDK сервісу вимагає свого воркера (OneSignal) або імпорту своєї бібліотеки (Braze). Якщо у вас вже є власне кешування і push handler, їх треба акуратно поєднати.
  • Привʼязка до вендора. Підписки зберігаються в сервісі, тож перехід на іншого постачальника або на власний бекенд означає повторну підписку частини користувачів. Це моє припущення зі специфіки Web Push (підписка привʼязана до ключів і постачальника), перевірте на своєму кейсі.
  • Дані й приватність. Перевірте, де зберігаються дані підписників і як це узгоджується з вашою політикою конфіденційності.
  • Нові можливості iOS. Якщо ви плануєте Declarative Web Push (iOS 18.4+), уточніть у документації

    💼 Розділ 8. Обхідні шляхи та fallback-стратегії

    Якщо PWA Push на iOS не підходить для вашого кейсу — email і in-app сповіщення залишаються надійнішою альтернативою, бо не залежать від установки PWA та стану push-підписки.

    PWA Push на iOS працює, але має обмеження: потрібна установка на домашній екран, можлива втрата підписок, а стабільність доставки, за досвідом розробників, нижча, ніж в Android і нативних додатках. Для критичних сповіщень варто мати fallback-стратегію.

    Надійна система сповіщень — це не вибір між Push і email, а комбінація каналів з розумною деградацією.

    Email як основний fallback

    Email не залежить від версії iOS або статусу установки PWA, а доставку можна відстежувати (bounce, відкриття, скарги). Для транзакційних сповіщень (оплата, підтвердження, важливі події) це надійніший канал. Обмеження: він працює лише для користувачів, чию адресу ви маєте, а доставка залежить від репутації вашого домену (SPF, DKIM, DMARC).

    In-app сповіщення

    Лічильник непрочитаних у заголовку, стрічка подій всередині додатку — це те що користувач гарантовано побачить при наступному відкритті PWA, незалежно від статусу push-підписки.

    Комбінований підхід з Kazki AI

    У Kazki AI використовується багаторівнева система:

    1. Push-сповіщення — для користувачів, які встановили PWA і дозволили сповіщення
    2. Badge на іконці (Badging API) — для встановлених PWA, як доповнення до push
    3. In-app стрічка подій — для всіх, хто відкриває додаток
    4. Email — для всіх користувачів як fallback і основний канал для транзакційних повідомлень

    Така комбінація значно розширює охоплення: користувач, який не встановив 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 як fallback для тих хто не встановив PWA
    • Бюджет не дозволяє паралельно розробляти нейтив-додаток

    Варто розглянути нейтив або гібрид якщо:

    • Сповіщення критичні: банківські транзакції, медичні алерти
    • Частка iOS у вашій аудиторії висока (орієнтир — понад половину користувачів)
    • Потрібні фонові процеси або геолокація у реальному часі
    • App Store presence є частиною маркетингової стратегії

    Висновок з досвіду Kazki AI

    У Kazki AI PWA Push на iOS працює задовільно для інформаційних сповіщень — "нова казка готова", "нагадування про підписку". Доставка на iOS менш стабільна, ніж на Android, але прийнятна для продукту, де критичність доставки не висока. Email залишається основним каналом для транзакційних сповіщень.

    Якщо ви ще вибираєте між PWA та нейтив-додатком і не визначились з архітектурою — детальне порівняння з реальними цифрами у статті PWA vs Native у 2026: порівняння та реальний кейс .

    📖 Джерела