Давайте знакомиться! Меня зовут Игорь. Я разработчик кроссплатформенных мобильных приложений в компании Smartex. В этой статье я расскажу о NativeScript, основных преимуществах платформы и ее недостатках.
Материал будет полезен не только разработчикам, которые уже пишут приложения на React Native, Ionic, Framework7, а также тем, кто собирается погрузиться в разработку мобильных приложений на JavaScript.
Погнали!
Свой путь в кроссплатформенную разработку я начал за пределами Smartex, с Cordova + Framework7.
Framework7 как и Ionic просто UI-библиотека для создания интерфейса, которая в последующем просто отрисовывается через WebView. Само приложение (общение с API платформы) создается на платформе Cordova или Capacitor, как раз они и создают экран с WebView и транслирует туда HTML.
Framework7 прекрасно работает с Vue, но по ряду причин он начал меня подбешивать. Мне хотелось найти инструмент именно для нативной разработки, а не на технологии основанной на WebView. Я стал смотреть в сторону Angular и на то, какие вообще есть на рынке технологии для разработки нативных кроссплатформенных приложений с Angular под капотом. Так я и познакомился с NativeScript.
Ниже представлены экраны приложения для работы с рисками Riskhunter сделано нами на платформе NativeScript, запущено в 2022 году и успешно работает на 18 фабриках.
Изображения экранов
Что такое NativeScript
NativeScript (NS) — это платформа для создания нативных кроссплатформенных мобильных приложений на JavaScript. NativeScript позволяет прямиком из JavaScript обращаться к API iOS/Android, минуя слой сторонних плагинов. Пожалуй, это самое главное отличительное преимущество перед его старшим братом React Native. NativeScript не позволяет разрабатывать десктопные версии приложений, как это могут делать Flutter и React Native, но работа над этим ведется. Возможно, она появится в ближайшем будущем. Также в отличие от React Native, который работает только на React, NativeScript позволяет использовать Angular, Vue, React, Svelte или ванильный JS/TS.
Кроссплатформенная разработка
Лагерь людей, занимающихся кроссплатформенной разработкой, делится на две части: тех, кто хочет сэкономить ресурсы, и тех, у кого ресурсов просто нет.
Первая причина выбрать кроссплатформенную разработку — получение двух результатов по цене не вдвое выше, чем один результат. Разработчик на Flutter создает приложение одновременно для iOS и Android, и даже для веба (правда пока только для нетребовательных к виду веб-приложения заказчиков). Один разработчик на C# может создать сразу два приложения, используя Xamarin — это делает разработку приложения дешевле и быстрее. Результат виден одновременно на всех платформах, приложение выглядит более-менее одинаково, и если это соответствует целям проекта — прекрасно.
Вторая причина — в команде уже есть разработчик на JavaScript/TypeScript. Работающий с React/Vue.js/Angular разработчик может создать неплохо выглядящее приложение, иногда даже повторно используя куски логики из уже существующего веб-приложения.
В моем случае роль сыграли оба фактора. На старте проекта в команде были фронтендеры и не было реалистичных сроков и бюджета.
Так как наша команда фронтендеров работает с Vue.js, вариантов было немного: основанные на HTML Ionic, Framework7, и основанный на нативных элементах NativeScript. Сразу скажу, что моё сердце по-прежнему принадлежит Angular, который полностью поддерживается в NativeScript.
От Ionic и Framework мы отказались из-за не-нативного поведения UI и ряда ограничений, хотя на таких платформах можно гораздо быстрее разрабатывать прототипы и доставлять новые фичи.
Мы выбрали NativeScript с Vue под капотом. Спустя пару лет разработки в такой комбинации у нас сложились смешанные ощущения, о которых я хочу поговорить.
Чтобы сразу убедить читателей в преимуществах NativeScript, начну с его минусов:
Очень маленькое сообщество. Активных пользователей сервера NativeScript в Discord можно пересчитать на пальцах. Рыночная доля NativeScript с 2019 по 2022 год упала с 11% до 3%.
Скудный набор плагинов по сравнению с React Native, а многие существующие уже давно не поддерживаются;
Из-за маленькой пользовательской базы даже небольшие поломки приводят к тому, что приходится долго траблшутить код или даже свою среду исполнения. Нередки случаи, когда проект просто перестает собираться с эзотерической ошибкой, без изменений в коде и без обновления среды — “сам по себе”. Собственные инструменты NativeScript не всегда прямо показывают где в подлежащем процессе произошла ошибка, и иногда просто прячут ее.
Ну вот, пожалуй, и всё!
А какие плюсы и киллер-фичи мы имеем?
К основным киллер-фичам я бы отнес три пункта:
NativeScript работает с основными JS-фреймворками (Angular, Vue, React, Svelte) и вовсе без фреймворка;
Прямой доступ к API iOS и Android из JavaScript/TypeScript без бойлерплейта — просто обращайся и пользуйся;
Чрезвычайно энергичная, общительная и активная ядерная команда. У них есть свой Discord, где они всегда готовы обсудить любой вопрос или запрос, и многие запрошенные мною вещи становились реальными фичами фреймворка — быстро и с обратной связью от команды.
Давайте чуть подробнее
Поддержка JS-фреймворков
NativeScript поддерживает основные фреймворки (Angular, Vue, React, Svelte), чистый (Plain) JS или TS (для тех, кто не признает фреймворки вовсе).
Angular и Plain поддерживались с рождения NativeScript, поэтому именно по ним самое большое количество примеров и вопрос на Stack Overflow. После появилась поддержка Vue, React и Svelte. По моему мнению, приложения на NativeScript лучше всего делать с Angular или чистым TypeScript.
Разработка мобильного приложения — это несколько больше, чем просто гонять данные с сервера на экран пользователя, как в вебе. Тут тебе и хранилища нескольких видов, работа с файлами, работа с аппаратной частью телефона, работа с сетью.
Использование Vue несколько усложняет разработку, потому что добавляется еще прослойка Vuex, и свои сервисы мы «дергаем»уже из Vuex.
Также с Vue приходится самому изобретать архитектуру проекта, и эта архитектура может кардинально отличаться от проекта к проекту.
Angular же предлагает готовую хорошо продуманную структуру проекта, свой DI, полную поддержку TypeScript а так же RxJs, которого хватает за глаза при разработке мобильного приложения. С ванильным TypeScript можно попытаться организовать что-то похожее.
Связь с core-командой: история из жизни
В одном личном проекте мне понадобился свайп для элемента списка, как в списке чатов Telegram или WhatsApp. Платформа предлагала один компонент для списков с поддержкой свайпов (https://v7.docs.nativescript.org/ui/components/radlistview/overview), но анимация была не та, которую хотелось бы видеть в проекте.
Мне пришлось сделать свой компонент, который я после показал основному разработчику NativeScript Nathan Walker.
В процессе диалога мы сделали вывод что, действительно нет альтернативы компоненту RadListView (ссылка выше), и что надо что-то делать. Спустя пару месяцев Натан опубликовал статью о том, как реализовать свайпы в CollectionView для iOS, а после и для Android.
В Discord парни из команды NativeScript охотно отвечают на вопросы. А еще каждый месяц 4-го числа в Discord проходит “Office Hours”. Команда рассказывает о процессе разработки NativeScript и отвечает на вопросы, которые можно задать по ходу стрима.
Доступ к API платформы
NativeScript позволяет обращаться к платформе прямиком из JS и это очень клево, так как дает возможность работать с платформой, не зная Java, Kotlin, Swift и тем более Objective-C.
Ниже разберем простой код и как к нему может прийти js-разработчик. Пример взят с официального сайта.
Допустим, появилась задача узнать уровень батареи. Мы идем в гугл и находим документацию https://developer.apple.com/documentation/uikit/uidevice.
Тут мы видим класс UIDevice и что он позволяет получить информацию о конкретном девайсе.
Далее мы переходим в наш редактор VS Code в класс компонента и начинаем печатать UIDevice. Редактор нам подсказывает,какие методы содержатся в классе UIDevice.
Мы видим все задекларированные классы, методы, свойства для конкретной платформы.
Ниже представлен полностью рабочий код для доступа к уровню батареи устройства iOS/Android не написав ни строчки на Swift или Java.
// iOS
const formatMessage = level => `The Battery Level is: ${level}%`;
const value = UIDevice.currentDevice.batteryLevel * 100;
alert(formatMessage(value));
// Android
const formatMessage = level => `The Battery Level is: ${level}%`;
const value = bm.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
alert(formatMessage(value));
А теперь давайте рассмотрим задачку посложней. Мы напишем мини-плагин для работы с SDK сервиса OneSignal для Android и попробуем обратиться к его методам.
Создаем в корне директорию plugins/onesignal со следующим содержимым:
Нас тут интересует plugins/onesignal/platfors/android/include.gradle и plugins/onesignal/index.android.ts - именно он будет работать на Android.
// include.gradle
dependencies {
implementation 'com.onesignal:OneSignal:[5.0.0, 5.99.99]'
}
// index.android.ts
import { Utils } from '@nativescript/core';
declare const com: any;
export class OneSignal {
// основная магиях происходит в этой строке
private _oneSignal = com.onesignal.OneSignal;
static init(): void {
this._oneSignal.initWithContext(Utils.android.getApplicationContext(),
'opensignal_app_id');
}
}
Теперь мы можем работать с классом OneSignal пакета com.onesignal из JavaScript. Что мы и делаем в методе init().
Вернемся к негативной стороне NativeScript ????
Очень маленькое сообщество. Core-team состоит меньше, чем из десяти человек. За каждой JS-платформой (NativeScript, Vue или NativeScript React) стоит чаще всего один человек. По Vue rigor789, По React shirakaba. Сообщество очень дружное и активное. Видеоуроков, примеров, решений на Youtube немного, но есть платные и бесплатные официальные видеокурсы.
Крошечный набор плагинов. Основные плагины есть, и они поддерживаются. Например, камера, Firebase, Couchbase, SQLite, биометрия, прием платежей и т.д. Со списком официальных плагинов можно ознакомиться https://docs.nativescript.org/plugins/. А вот тот же OneSignal давно перестал обновляться. Но у нас есть наш волшебный доступ к API, и мы просто добавляем библиотеку в app.gradle, пишем немного JavaScript и вуаля!
В общем, плагинов мало, но их можно легко создать.
Периодически что-то ломается. В проектах на Vue NativeScript и Angular NativeScript у меня временами отваливается hot reload. Приходится тормозить консоль и перезапускать.
Встречаются ошибки при малейшем обновлении какого-либо пакета, проект тупо не запускается — но это частая проблема проектов на npm.
Особенности. Советы. Лайфхаки
При разработке на NativeScript мы находимся очень близко к нативным элементам UI и к API самой платформы. Волей-неволей приходится учить нативную разработку, хотя бы одну платформу, и хотя бы поверхностно.
Как называются элементы, какие у них есть свойства и так далее? Благодаря этому вы будете чувствовать “на кончиках пальцев”, что вообще происходит в вашем приложении.
Рекомендую подглядывать за кодом самих разработчиков NativeScript при разработке плагина или при работе с нативным API. Это помогает быстрее развивать скиллы.
И на финал. Конечно, NativeScript не пользуется такой широкой поддержкой как React Native, но благодаря своей простоте с NativeScript может работать фронтендер без специальной подготовки. Также в силу своей архитектуры NativeScript позволяет “вытягивать” в том числе весьма большие и сложные проекты.
Полезные ресурсы:
Комментарии (12)
DamirMur
27.12.2023 07:26Не подскажете, ссылку не дадите, как правильно билдить в прод?
Bezlepkin Автор
27.12.2023 07:26iOS/Android?
DamirMur
27.12.2023 07:26+1Желательно и то и другое
Bezlepkin Автор
27.12.2023 07:26Правильней нет. Есть проще и сложней :)
Если нужно быстро iOS, я просто открываю в Xcode, делаю архив и выгружаю в TestFlight. Android через CLI
https://docs.nativescript.org/guide/publishing/android-google-play
А так мы настроили Fastlane, который запускается через CI/CD гитлаба. Он делает все сборки, отправляет в TestFlight, BrowserStack, уведомления в Slack, вообщем полная автоматизация. На эту тему я тоже напишу статью.
Вот есть статья что бы понимать как это примерно делается
https://blog.nativescript.org/automatic-nativescript-app-deployments-with-fastlane/
Но можно настроить гораздо шире.DamirMur
27.12.2023 07:26Спасибо, но там многое не описано. Куда например поместить значок приложения, куда картинку для экрана загрузки, как правильно вставлять экраны для рекламы. Нет ключей оптимизации (возможно они не нужны), но я где-то встречал и упустил, что если вставить какой-то ключ, то полностью отключаются все дебаги и программа работает быстрее.
DamirMur
27.12.2023 07:26+18 шагов для публикации вашего приложения NativeScript в магазинах приложений -https://blog.nativescript.org/steps-to-publish-your-nativescript-app-to-the-app-stores/
DamirMur
У меня смешанные чувства от использования Nativescript. С одной стороны очень удобная оболочка-посредник, с другой стороны низкая скорость. Сделал функцию на JS календарь, все события уже рассчитаны, нужно, с учетом таймзоны, локали, разделить-локализовать по месяцам и по дням и вывести. На андройде Samsung Galaxy a32. пересчёт одного месяца занимает 2.5-4.0 секунды, пересчет всего года 45-60 секунд, и это без вывода, просто массив данных, что очень много. Потому что когда напрямую с сайта грузит, всё тоже самое, и пересчитывает месяц в браузере - 0.015-0.025 сек, там даже пересчитывать не надо, вставляется на лету. Пришлось webView вставить и в нем на автономной страничке в скрипте с такой же функцией считать, тогда скорость сравнимая с сайтом.
Но с таким же успехом можно и на каком-нибудь NoCode типа AppInventor ваять, накидал путь из кубиков от менюшки до WebView и потом только вставляй странички со скриптами и стилями.
Bezlepkin Автор
хххммм... тут надо разбираться, так как NativeScript тут скорее всего не при чем. Возможно ты где то блокируешь поток?!
DamirMur
Нет, я всё пробовал и Promise и setTimeout и async-await, что здесь заблокируешь {month.details = C.getTextMonth(i)} , замер идет в самой функции.
Ключ {markingMode: 'none'} Может, что ещё отключить, при компиляции? Я много чего перерыл, где-то упоминалось что на ios быстрее работает, чем на android. Типа дело в работе с движком V8, а в браузере всё давно вылизано, нативно, потому и летает.
Там, правда функция локализации времени на java написано для android, я отключал мало чем помогло. Плагин intl - так себе.