Что нужно сделать, чтобы в браузере появилась кнопка скачать PWA
Что проверяют
Понимаете ли вы, что эту кнопку рисует не браузер, а вы сами, и что появляется она только когда сайт прошёл критерии установки. Попутно проверяют, знаете ли разницу в поддержке между браузерами.
Как отвечать
Сразу поправить формулировку: приложение не скачивается, а устанавливается из уже открытой вкладки. Условия:
- сайт отдаётся по HTTPS, на localhost тоже работает;
- в разметке есть
<link rel="manifest">, а в манифесте заполненыname,short_name,start_urlвнутриscope,displayсо значениемstandaloneи иконки на 192 и 512 точек, лучше сpurposeравнымmaskable; - зарегистрирован service worker, который контролирует
start_url. Точные требования браузеры меняли, поэтому итог сверяют не по памяти, а во вкладке разработчика с отчётом об устанавливаемости.
Дальше событие и своя кнопка:
let deferred = null;
window.addEventListener("beforeinstallprompt", (e) => {
e.preventDefault(); // иначе браузер покажет свой баннер
deferred = e;
installButton.hidden = false;
});
installButton.addEventListener("click", async () => {
installButton.hidden = true;
deferred.prompt(); // только внутри обработчика клика
await deferred.userChoice;
deferred = null; // событие одноразовое
});Ещё упоминают событие appinstalled и проверку matchMedia("(display-mode: standalone)"), чтобы не показывать кнопку тем, кто уже установил приложение.
Где спотыкаются
Ждут одинакового поведения везде. Событие beforeinstallprompt реализовано в браузерах на Chromium, в Safari его нет вовсе: там установка идёт вручную через меню, и нужна своя подсказка, а не кнопка.
Сохраняют событие и вызывают prompt() позже: по таймеру, при скролле, повторно на том же объекте. Браузер такой вызов отклоняет, потому что нужен жест пользователя, а событие срабатывает один раз, после выбора надо дождаться нового.
Правят только манифест и не смотрят остальное. Сайт на HTTP, start_url вне области действия, иконки не тех размеров, service worker зарегистрирован из вложенного пути и корень не контролирует. Событие в таком случае просто не приходит, а причина видна в панели разработчика.