До этой главы PWA выглядела как набор инструментов, которые «просто работают». На практике у технологии есть набор тонких мест, на которые наступают почти все. Разберём четыре главных: ограничения iOS Safari, проблема «обновление не подхватывается», что показывает Lighthouse-аудит и как отлаживать service worker в DevTools.

Что не работает в iOS Safari

iOS — самая капризная платформа для PWA. Apple исторически не заинтересована в том, чтобы веб конкурировал с App Store, поэтому многие возможности либо работают с задержкой, либо урезаны.

На что наступают чаще всего:

  • Push-уведомления — только с iOS 16.4. На более ранних версиях pushManager.subscribe() просто бросает ошибку. И даже на новых iOS push работает только если PWA установлено на главный экран — в обычной вкладке Safari push не работает.
  • Нет badging. navigator.setAppBadge() на iOS Safari отсутствует. Значок «3 непрочитанных» на иконке показать нельзя.
  • Background sync не поддерживается. Если задача в офлайн-режиме должна автоматически отправиться, когда сеть вернётся, — на iOS придётся отправлять её при следующем открытии приложения, не «в фоне».
  • Web Share Target недоступен. Установленное PWA не появляется в системном меню «Поделиться».
  • Квота хранилища меньше и сбрасывается. Safari ограничивает Cache API и IndexedDB примерно 1 ГБ, и может очистить хранилище, если приложением не пользовались неделю-две. Никаких гарантий на сохранность кэша между визитами нет.
  • Service worker засыпает быстрее. Safari агрессивно убивает SW спустя 30 секунд бездействия, в то время как Chrome держит дольше. Длительные задачи в SW — ненадёжный паттерн на iOS.

Практический вывод: не стройте критические для бизнеса флоу на одних только PWA-возможностях, если ваша аудитория — iPhone. Push-канал дублируйте email/SMS, важные данные синхронизируйте при следующем заходе вручную, не полагайтесь на background sync.

Проблема «обновление не подхватывается»

Типичная ситуация: разработчик задеплоил новую версию сайта, открывает свой PWA — видит старую. Перезагрузил страницу — всё равно старая. Закрыл вкладку, открыл заново — старая. Почистил кэш в DevTools — наконец-то новая.

Причина в том, как именно service worker управляет обновлениями. По умолчанию:

  1. Браузер скачивает новый sw.js, обнаруживает, что он отличается от установленного.
  2. Новая версия SW переходит в состояние waiting.
  3. Старая версия SW продолжает контролировать все открытые вкладки до тех пор, пока пользователь их все не закроет.
  4. Только после полного закрытия новая версия активируется и берёт управление.

Это поведение хорошо для стабильности (пользователь не получит разную JS-логику в открытых вкладках одновременно), но неудобно для развёртывания. Лекарство — явное обновление имени кэша при каждом деплое:

const BUILD_VERSION = "a3b9f4c"; // короткий git-хэш коммита, подставляется на сборке
const CACHE_NAME = `pwa-cache-${BUILD_VERSION}`;

Если CACHE_NAME поменялся — на новой версии SW в install положится в кэш свежая копия всех файлов, а в activate удалятся старые. Главное — что само имя файла sw.js остаётся прежним, поэтому браузер замечает изменение содержимого и инициирует обновление.

В CI это автоматизируют скриптом, который вписывает в sw.js хэш текущего коммита перед сборкой:

// build.js
const fs = require("fs");
const { execSync } = require("child_process");

const hash = execSync("git rev-parse --short HEAD").toString().trim();
let sw = fs.readFileSync("src/sw.js", "utf8");
sw = sw.replace("__BUILD_HASH__", hash);
fs.writeFileSync("dist/sw.js", sw);

В коде SW при этом стоит плейсхолдер const BUILD_VERSION = "__BUILD_HASH__";, который заменяется на реальный хэш на этапе сборки.

Если хочется ускорить обновление ещё сильнее — добавить self.skipWaiting() в install и self.clients.claim() в activate, как разбирали в главе 4. Но это с оговорками: открытые вкладки получат новую версию SW резко, и если в них уже была загружена JS-логика старой версии, может появиться рассинхронизация.

Lighthouse PWA audit

Lighthouse — встроенный в Chrome DevTools аудит производительности. Кроме performance, accessibility и SEO у него есть категория PWA, которая проверяет десяток условий: HTTPS, валидный манифест с обязательными полями, иконки правильных размеров, зарегистрированный service worker, оффлайн-фолбэк и так далее.

Запуск: открыть DevTools (Ctrl+Shift+I или Cmd+Opt+I) → вкладка Lighthouse → выбрать категорию «Progressive Web App» → кнопка «Analyze page load». Через 20–40 секунд появится отчёт со списком пройденных и не пройденных проверок.

Самые частые провалы:

  • Нет иконки 512×512 — Chrome не покажет кнопку установки без неё.
  • Нет background_color или theme_color в манифесте.
  • Service worker не контролирует start_url — обычно потому, что scope SW уже, чем директория start_url.
  • Сайт не отвечает кодом 200, если выключить сеть — то есть нет offline.html в кэше.

Пройдите Lighthouse-аудит до того, как считать PWA готовым к выкатке.

Отладка в DevTools

Главный инструмент — вкладка Application в Chrome или Firefox DevTools.

Полезные секции:

  • Manifest. Распарсенный манифест, превью иконок, ошибки и предупреждения. Первое место, куда смотреть, если «кнопка установки не появляется».
  • Service Workers. Список зарегистрированных SW, кнопки Update (форсировать проверку обновления), Skip waiting (активировать ждущую версию), Unregister (полностью снять SW). Чекбокс Update on reload — на этапе разработки бесценен: каждое обновление страницы будет переустанавливать SW, не нужно вручную закрывать вкладки.
  • Cache Storage. Все именованные кэши с их содержимым. Можно вручную удалить запись и проверить, что произойдёт.
  • Storage. Кнопка Clear site data — полностью обнулить состояние: снять SW, очистить кэши, очистить IndexedDB. Удобно для проверки «как сайт выглядит для нового пользователя».

В консоли service worker запускается в собственном контексте — в DevTools его консоль доступна через выпадающий список «top frame» в правом верхнем углу, нужно переключиться на sw.js. console.log внутри SW попадает именно туда, а не в основную консоль страницы.

[СКРИНШОТ: Chrome DevTools → вкладка Application → раздел Manifest с распарсенным манифестом и превью иконок справа, ниже — раздел Service Workers с активным SW и чекбоксом «Update on reload».]

Чек-лист перед выкаткой PWA в прод

  1. HTTPS на проде; на staging тоже — иначе SW там не зарегистрируется.
  2. Манифест проходит Lighthouse PWA audit без ошибок.
  3. В sw.js имя кэша содержит версию/хэш сборки.
  4. В install предзагружается offline.html; в fetch есть fallback на него.
  5. В коде есть проверка isStandalone для подсказки iOS-пользователям, как установить.
  6. На push-уведомлениях не построены критические флоу для iOS-аудитории — есть запасной канал.
  7. В аналитику отправляется событие appinstalled, чтобы видеть процент установок.

Если все семь пунктов выполнены — ваше PWA готово.