Одним из существенных недостатков Thunkable X является отсутствие механизма обмена данными между WebViewer и app (нативной частью приложения). При помощи URI по протоколу data: во многих случаях мы можем передавать данные из app в WebViewer, но не обратно из WebViewer в app. Из-за этого невозможно использовать web-технологии и фреймворки для расширения скромной функциональности Thunkable X и создания удобных пользовательских интерфейсов при помощи jQuery Mobile, Framework7 и др. Остаётся либо ждать, пока разработчики не добавят новые компоненты, либо самим реализовать столь необходимый обмен данными.
Для организации обмена данными между WebViewer и app необходима область, к которой могут получить доступ оба этих компонента. И WebVioewer, и app имеют доступ к собственным локальным хранилищам, но обмен данными между ними невозможен. Также невозможна работа и через файловую систему. Остаётся единственный вариант обмена данных через web-сервер. C web-сокетами Thunkable X работать не умеет, но при помощи компонента Web API можно отправлять запросы http-серверу.
В качестве web-сервера воспользуемся сервисом postman-echo.com. Изюминка этого сервиса состоит в том, что он сохраняет данные запроса в куки, благодаря чему можно реализовать такой алгоритм:
Использование куки для хранения данных накладывает большие ограничения и на объём передаваемых данных, но это не самая большая проблема.
Запрограммировать показанный алгоритм можно двумя способами - при помощи задержки или таймера. В первом случае между отправкой данных и их получением ставится задержка, например, 1 секунда. Это с какой-то степенью вероятности будет гарантировать то, что данные успеют записаться на сервере прежде, чем мы их запросим их. Во втором случае при помощи таймера производится регулярный опрос сервера на предмет наличия в нём сохранённых данных.Преимущество использования задержки - простота. Недостатки - медленный обмен и невысокая надёжность, которая непосредственно зависит от качества соединения. Отсюда высокая вероятность того, что отправленные данные вообще не будут получены.
Преимущество использования таймера - более высокая надёжность по сравнению с использованием задержки, недостатки - медленный обмен и высокая нагрузка на сервер из-за постоянных запросов. Последний недостаток можно устранить, запуская таймер в момент отправки данных и выключая его при их получении. Но это возможно в том случае, когда данные отправляются из app в WebViewer, так как из WebViewer запустить таймер приложения невозможно.
Чем ниже скорость обмена данными web-сервером, тем хуже отзывчивость интерфейса. Для её улучшения можно сократить интервал времени для таймера, но это может привести к блокировке IP из-за слишком частого обращения к серверу. Сервисы реального времени позволяют производить опрос не чаще 1 раза в секунду. На этот интервал опроса я бы и ориентировался. Если у вас есть свой ненагруженный web-сервер, то можно попробовать уменьшить интервал опроса до 0,3 сек или установить ещё меньшее значение.
При взаимодействии с интерфейсом задержка 1 сек является слишком большой, поэтому остановимся на примере обработки данных. Для этого можно использовать методы или полифункции, но мы остановимся на самом мощном и универсальном варианте - функции генерации кода Javascript из строки. В демонстрационных целях я выбрал функцию eval(), но вместо неё вы можете сразу использовать new Function().
При нажатии на кнопку btnSend вызывается функция setData2, в которую передаётся строка из поля ввода, для её отправки. Код для вставки в текстовые блоки показан ниже.
data:text/html,<meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> function send(){ document.frmMain.test.value = myData; document.frmMain.submit(); } </script> <body> <iframe name="hidden" style="display:none"></iframe> <form name="frmMain" action='https://postman-echo.com/cookies/set/' method='get' target='hidden'> <input value="" type="hidden" id="test" name="test" placeholder="enter screen2"> </form> <script>send();</script>
</body>
В блоке приёма данных сначала производится проверка на существование нужного поля test (и его создания, в случае отсутствия), а затем отображение полученных данных в компоненте Label.
Этот блок подойдёт и для работы по таймеру вместо задержки. Переменная oldValue используется для создания защёлки. Без неё данные будут постоянно обновляться в lblResult при каждом срабатывании таймера, что можно отследить при помощи компонента lblTimerTick.
Более скоростным, надёжным, но и более сложным является обмен данными через Firebase. Для доступа к этой базе данных из WebViewer используется Firebase Javascript API, а в приложении можно использовать глобальные переменные с типом хранения cloud или более универсальные блоки компонента Realtime DB. Преимущество Firebase состоит в том, что в нём используется событийная модель управления данными, при которой последние обновляются автоматически при их изменении на стороне сервера или клиентов.
Если ваша база данных Firebase является публичной, то сложностей не возникает. Нужно только для каждого клиента выделить свой узел для обмена данными. Если они все будут работать через один узел, то с ростом числа клиентов будет увеличиваться вероятность перезаписи данных друг друга.
При использовании частной базы всё усложняется. Для доступа к ней из javascript-кода нужно указать параметры соединения и данные для входа в аккаунт. То есть, придётся делать два входа в базу: один на стороне WebViewer, второй - на стороне app. Избежать этого можно, если вместо ресурсного html-файла весь код реализовать внутри WebViewer при помощи протокола data:text/html, как это было показано в примере выше. Тогда учётные данные можно будет передать в WebViewer после успешного входа в базу при помощи компонента Sing In.
Базовый пример html-файла. Все пустые строки в верхней части кода нужно заполнить данными доступа к вашей базе.
В качестве корневого узла пользователя используется его идентификатор в базе. Приём данных происходит через поле get, а обработанные данные записываются в поле set. В приложении нужно создать переменные типа cloud, имена которых будут следующими:
userId/get - для отправки данных
userId/set - для приёма обработанных данных
, где userId можно получить в блоке Sing In в случае успешного подключения пользователя к базе Firebase.
Как видите, решение задачи по обработке данных не представляет большой сложности. При желании можно код показанного выше html-файла перенести в WebViewer, а вот код фреймворков внутри WebViewer, возможно, работать не будет и тогда придётся его вместе с кодом обмена данными сохранять в ресурсном html-файле. При изменении этого файла придётся каждый раз загружать его в проект заново, как это делается при работе с расширениями .aia в Thunkable Classic, App Inventor и др.