Веб-библиотека для работы в вебвью «Нашего нативного приложения».
Особенности реализации вебвью модуля нативного приложения требуют определенных действий в т.ч. на сервере. В Библиотеке реализована необходимая логика для любых веб-серверов на базе Node.js Если в вашем веб приложении нет Node.js слоя, придётся реализовать необходимую логику самостоятельно. Разобраться, что нужно от сервера можно с помощью этого документа.
- Как использовать Библиотеку описано здесь.
- Про особенности навигации в вебвью окружении нативного приложение можно прочитать здесь.
- Про работу в мини-приложениях (без экземпляра
BridgeToNative) можно прочитать здесь. - Схематичное client-server взаимодействие внутри библиотеки описано здесь.
BBtn(BackButton) — Кнопка «Назад» в нативной части экрана, которая может работать, как «Назад» в браузере.B2N(BridgeToNative) — сокращенное название данной Библиотеки.NA(Native application) — нативное приложение, для WebView которого написана эта библиотека.WA(Web application) — любое веб-приложение, которое открывается внутри WebView.WV(WebView) — область на экране нативного приложения, которая умеет отображать веб-контент.
Legacy— так обозначается старый способ взаимодействия веб-натив, черезsetPageSettings, а также подход с передачей параметров о NA через query в URL и кастомные HTTP-заголовки.Modern— новый контракт взаимодействия веб-натив/натив-веб, в противовесLegacy, от которого постепенно будем отказываться.
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(отдельная точка входаclient/primitives).
Обычно на клиентской стороне достаточно экземпляра класса BridgeToNative.
Это фасад, который проксирует все публичные методы Библиотеки внутренних классов.
Исключение — чистые функции из client/primitives:
их же использует 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 появился ещё один режим WV — мини-приложения в нативном контейнере. BridgeToNative
в этом контейнере нет.
Для этого режима есть отдельная точка входа @alfalab/bridge-to-native/client/primitives.
В ней лежат кирпичики, из которых собран сам BridgeToNative: чистые функции сборки нативных URL
и проверки версий NA — всё, чему не нужен экземпляр, состояние и window. Класс их же и использует,
поэтому результат совпадает, а код класса в эту точку входа не попадает.
Вход не ограничен мини-приложениями: подойдёт любому WA, которому нужны только эти функции.
Их же экспортирует и @alfalab/bridge-to-native/client.
Платформу, appId и версию NA потребитель определяет сам; навигацию (location.replace), закрытие WV
и защиту от повторных вызовов — тоже.
import {
canUseNativeFeature,
isVersionHigherOrEqual,
prepareNativeDeeplinkUrl,
prepareOpenInBrowserUrl,
prepareOpenInNewWebviewDeeplink,
preparePdfUrl,
type NativeAppTarget,
} from '@alfalab/bridge-to-native/client/primitives';
const target: NativeAppTarget = { platform: 'ios', appId: 'aweassist' };
prepareNativeDeeplinkUrl(target, 'alfabank:///dashboard/pfm'); // 'aweassist://pfm?fromCurrent=true'
// Флаг — результат проверки версии NA или `true`, если NA заведомо умеет открывать ссылки в браузере.
const linksInBrowser = canUseNativeFeature('ios', '13.3.0', 'linksInBrowser');
prepareOpenInBrowserUrl(target, 'https://ya.ru', linksInBrowser); // 'https://ya.ru/?openInBrowser=true'
prepareNativeDeeplinkUrl(target, prepareOpenInNewWebviewDeeplink('https://ya.ru', 'Заголовок'));
preparePdfUrl(target, 'https://example.com/file.pdf', 'pdfFile', 'Выписка');prepareNativeDeeplinkUrlотрезает у диплинка один из префиксов/,alfabank:///dashboard/,alfabank:///,alfabank://,https://online.alfabank.ru/; остальные схемы и хосты не трогает. На iOS добавляетfromCurrent=true.prepareOpenInBrowserUrlиprepareOpenInNewWebviewDeeplinkпринимают абсолютныеhttp(s)URL и безопасные нативные диплинки (например,alfabank://webFeature?...). Бросаютinvalid url: …дляjavascript:,data:,file:,blob:,vbscript:,ftp:и URL с логином/паролем.isVersionHigherOrEqualсравнивает версии форматаx.y.zпокомпонентно, как числа; версия в другом формате даётfalse. Нужен для гейтов по версии NA за пределами известных фич.
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.
В BridgeToNative добавлены методы getGeoConfiguration, vibrate, setSheetDragArea,
hideTabbar и showTabbar: для получения конфигурации геолокации, тактильного отклика,
настройки перетаскивания нативной шторы и управления таббаром.
Эти методы доступны в iOS начиная с версии 17.0.0.
Для всех методов NativeCommandsService (getGeoConfiguration, vibrate, setSheetDragArea,
hideTabbar, showTabbar) должен быть включён общий фича-тогл (mainSuperAppIOS для iOS).
Он включает архитектуру мини-приложений, при которой WV открываются в шторах.
Для vibrate дополнительно требуется нативный фича-тогл webViewHapticsIOS.
Без соответствующих включённых ФТ функциональность не работает: NA игнорирует команды.
Библиотека не имеет доступа к нативным ФТ и проверяет только платформу и версию NA.
На Android или iOS ниже 17.0.0 использование команд вызовет предупреждение.
В новой версии с мини-приложениями можно столкнуться с багом при наличии перетаскиваемых элементов в WA. Шторка может перехватывать жесты, и ранее работавшее перетаскивание в WA может перестать работать. Для таких экранов ограничьте область перетаскивания только через навигационную панель:
window.b2n.setSheetDragArea('navigationBar');Для выпуска новой версии библиотеки в 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.
