Веб-библиотека для работы в вебвью «Нашего нативного приложения».
Особенности реализации вебвью модуля нативного приложения требуют определенных действий в т.ч. на сервере. В Библиотеке реализована необходимая логика для любых веб-серверов на базе Node.js Если в вашем веб приложении нет Node.js слоя, придётся реализовать необходимую логику самостоятельно. Разобраться, что нужно от сервера можно с помощью этого документа.
- Как использовать Библиотеку описано здесь.
- Про особенности навигации в вебвью окружении нативного приложение можно прочитать здесь.
- Схематичное client-server взаимодействие внутри библиотеки описано здесь.
BBtn(BackButton) — Кнопка «Назад» в нативной части экрана, которая может работать, как «Назад» в браузере.B2N(BridgeToNative) — сокращенное название данной Библиотеки.NA(Native application) — нативное приложение, для WebView которого написана эта библиотека.WA(Web application) — любое веб-приложение, которое открывается внутри WebView.WV(WebView) — область на экране нативного приложения, которая умеет отображать веб-контент.
WV NA окружение отличается от браузерного, есть определенные сложности при работе внутри. Главная сложность — навигация в этом окружении.
Вот несколько особенностей:
- Экран с WV содержит как UI-элементы NA, так и область для отрисовки веб-контента;
- WA может влиять на поведение NA;
- Поведение WV модуля NA в iOS и WV модуля NA в Android отличается во многих сценариях;
- Пользователи используют разные версии NA (особенно в iOS), разные версии NA имеют свои особенности.
Данная Библиотека:
- умеет собирать данные о NA, внутри которого она запущена;
- учитывает вышеуказанные «особенности»;
- предоставляет единый интрефейс для работы внутри NA разных версий, разных ОС.
Состоит из двух частей:
- серверная часть:
- необходима, чтобы распарсить запрос от NA и сохранить данные в cookie для клиентской части;
- реализована на Node.js;
- стараемся поддерживать совместимость с различными Node.js фреймворками и веб-серверами;
- обработку запросов с WV на других ЯП/технологиях придётся делать руками 🙂 (см. логику
src/server/prepare-native-app-details-for-client.ts)
- клиентская часть:
- предоставляет методы для работы WA внутри WV NA.
На клиентской стороне потребителям Библиотеки нужно использовать только экземпляр класса BridgeToNative.
Это фасад, который проксирует все публичные методы Библиотеки внутренних классов.
Проксируемые публичные методы не описаны по месту их реализации, описание находится в классе BridgeToNative (чтобы не повторяться). А все приватные методы описаны (если есть необходимость) в месте их реализации.
npm i @alfalab/bridge-to-nativeimport { isWebviewEnv, prepareNativeAppDetailsForClient } from '@alfalab/bridge-to-native/server';
//...
app.get('/my-ssr-route', (req, res) => {
// B2N проверит, что запрос пришёл из WV окружения.
// Можно не использовать, если обработчик обслуживает запросы только из WV.
const isWebview = isWebviewEnv(req);
if (isWebview) {
// B2N соберёт из запроса детали о NA и установит cookie для клиента.
prepareNativeAppDetailsForClient(req, (headerKey, headerValue) => {
res.set({
headerKey: headerValue,
});
});
}
// Что-то делаем ещё и отправляем ответ клиенту...
});import { BridgeToNative } from '@alfalab/bridge-to-native/client';
// Лучше использовать один экземпляр-синглтон.
window.b2n = new BridgeToNative();
// ...// ...
window.b2n.setTitle('Заголовок для верхней панели NA');
// ...
window.b2n.openInBrowser('https://ya.ru');
// ...NA при открытии экрана c WV вместе с областью для веб-контента отображает свои UI-элементы. Один из которых — кнопка «Назад» (BBtn), которая может работать, как «Назад» в Браузере, а может закрыть VW. Чтобы синхронизировать работу BBtn с историей браузера, необходимо для навигации а рамках WA использовать ТОЛЬКО следующие методы B2N:
navigateClientSide— soft навигация вперед (History: pushState())navigateServerSide— hard навигация вперед (Location: assign())goBack— soft или hard навигация назад (History: back())goBackAFewSteps— навигация на несколько шагов назад (History: go(-x)); С версии1.4.0подходит и для hard навигации, в т.ч. в рамках разных origin (см. описание в JSDoc метода).replaceHistoryState— заменаhistory.stateи/или URL текущей записи (History: replaceState())
Каждый метод содержит JSDoc c описанием назначения и ограничений.
// Только что открылось WV, находимся на /page-1,
// Если нажать BBtn, экран с WV будет закрыт.
window.b2n.navigateClientSide('/page-2');
// Находимся на /page-2,
// Если нажать на BBtn, она сработает как «Назад» в браузере, вернув на `/page-1`.
window.b2n.navigateClientSide('/page-3');
// Находимся на /page-3,
// Если нажать на BBtn, она сработает как «Назад» в браузере, вернув на `/page-2`.
window.b2n.goBack();
// Вернулись на /page-2,
// Если нажать на BBtn, она сработает как «Назад» в браузере, вернув на `/page-1`.
window.b2n.goBack();
// Вернулись на /page-1,
// Если нажать BBtn, экран с WV будет закрыт.-
Особенности WV NA заставляют WA хранить состояние синхронизации с NA. При использовании server-side навигации это состояние теряется, приходиться как-то передавать его дальше. Это на себя берёт Библиотека.
-
Внутри NA такие переходы как правило имеют плохой UX (низкая отзывчивость). WV-браузер продолжает показывать исходную страницу пока не получит ответ на запрос целевой страницы, а NA не показывает никакой индикации. После получения ответа на HTML запрос:
- NA в iOS и дальше не показывает никакой индикации.
- NA в Android начинает показывать нативный спиннер с фоном (полностью перекрывает веб-контент) пока грузятся «синхронные» ресурсы из HTML.
-
Также при переходе server-side навигацией целевое WA может быть недоступно по какой-либо причине. NA никак не обрабатывает такие ошибки.
-
BBtn никогда не блокируется, пользователь может бесконтрольно использовать её, во время процесса навигации. До версии
1.4.0в случае, если была использована hard навигация (navigateServerSide), этот сценарий сломает синхронизацию с NA. В версии1.4.0эта «особенность» учитывается, но потребители B2N должны использовать метод БиблиотекиreplaceHistoryStateвместо прямых вызововhistory.replaceState(или оберток над ним, типаhistory.replaceиз ReactRouter).
Из-за «второго и третьего пунктов» использовать server-side навигацию не рекомендуется. Переход к другому WA лучше делать через открытие нового WV (см. метод
openInNewWebview). Хотя делать это тоже нужно осторожно, т.к. каждое открытое WV утилизирует ресурсы устройства.
B2N предоставляет метод navigateServerSide для server-side навигации. Но WA должно учитывать «второй пункт»
и показывать пользователю какую-то индикацию перехода, блокировать кнопки (BBtn заблокировать нет возможности) и т.п.
При этом «Третий пункт» не решить на стороне веба и надо иметь его ввиду.
При navigateServerSide библиотека переиспользует уже существующий query device_app_version,
чтобы целевое WA могло восстановить appVersion даже если на следующем HTML-запросе не будет
заголовка app-version. Исторически этот query приходит из iOS, но B2N сознательно не вводит
для этого отдельный B2N-специфичный параметр и использует его как transport-поле и для Android.
Для выпуска новой версии библиотеки в npm необходимо создать GitHub Release. При этом нужно:
- Указать semver тег (major.minor.patch).
- Заполнить
Release notesв формате Markdown, описав изменения в разделах: Features, Bugfixes, BREAKING CHANGES (см. пример в CHANGELOG.md). Не используйте заголовки первого уровня при описании изменений, так как они используются для основных заголовков версии.
После публикации GitHub Release автоматически запускается workflow, который:
- Обновляет версию в
package.jsonсогласно тегу; - Обновляет
CHANGELOG.md; - Создает релизный Pull Request с этими изменениями со специальным тегом release.
После мержа релизного PR запустится следующий workflow, который:
- Опубликует версию в npm;
- Добавит архив с актуальными ассетами в созданный релиз. Таким образом ассеты с актуальным
CHANGELOG.mdбудут лежать в архиве release-x.y.z.zip
Для выпуска beta-версии достаточно добавить в сообщение коммита строку deploy_beta. Выпущенная beta-версия будет автоматически опубликована с тегом beta.
Выпущенную бета-версию можно посмотреть в разделе Actions в соответствующем workflow.