Давайте знакомиться! Меня зовут Игорь. Я разработчик кроссплатформенных мобильных приложений в компании 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, начну с его минусов:

  1. Очень маленькое сообщество. Активных пользователей сервера NativeScript в Discord можно пересчитать на пальцах. Рыночная доля NativeScript с 2019 по 2022 год упала с 11% до 3%.

  2. Скудный набор плагинов по сравнению с React Native, а многие существующие уже давно не поддерживаются;

  3. Из-за маленькой пользовательской базы даже небольшие поломки приводят к тому, что приходится долго траблшутить код или даже свою среду исполнения. Нередки случаи, когда проект просто перестает собираться с эзотерической ошибкой, без изменений в коде и без обновления среды — “сам по себе”. Собственные инструменты NativeScript не всегда прямо показывают где в подлежащем процессе произошла ошибка, и иногда просто прячут ее. 

Ну вот, пожалуй, и всё!

А какие плюсы и киллер-фичи мы имеем?

К основным киллер-фичам я бы отнес три пункта:

  1. NativeScript работает с основными JS-фреймворками (Angular, Vue, React, Svelte) и вовсе без фреймворка;

  2. Прямой доступ к API iOS и Android из JavaScript/TypeScript без бойлерплейта — просто обращайся и пользуйся;

  3. Чрезвычайно энергичная, общительная и активная ядерная команда. У них есть свой 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 позволяет “вытягивать” в том числе весьма большие и сложные проекты.

Полезные ресурсы:

https://nativescript.org

https://nativescripting.com

https://nativescript-vue.org

Комментарии (12)


  1. DamirMur
    27.12.2023 07:26

    У меня смешанные чувства от использования Nativescript. С одной стороны очень удобная оболочка-посредник, с другой стороны низкая скорость. Сделал функцию на JS календарь, все события уже рассчитаны, нужно, с учетом таймзоны, локали, разделить-локализовать по месяцам и по дням и вывести. На андройде Samsung Galaxy a32. пересчёт одного месяца занимает 2.5-4.0 секунды, пересчет всего года 45-60 секунд, и это без вывода, просто массив данных, что очень много. Потому что когда напрямую с сайта грузит, всё тоже самое, и пересчитывает месяц в браузере - 0.015-0.025 сек, там даже пересчитывать не надо, вставляется на лету. Пришлось webView вставить и в нем на автономной страничке в скрипте с такой же функцией считать, тогда скорость сравнимая с сайтом.
    Но с таким же успехом можно и на каком-нибудь NoCode типа AppInventor ваять, накидал путь из кубиков от менюшки до WebView и потом только вставляй странички со скриптами и стилями.


    1. Bezlepkin Автор
      27.12.2023 07:26

      хххммм... тут надо разбираться, так как NativeScript тут скорее всего не при чем. Возможно ты где то блокируешь поток?!


      1. DamirMur
        27.12.2023 07:26

        Нет, я всё пробовал и Promise и setTimeout и async-await, что здесь заблокируешь {month.details = C.getTextMonth(i)} , замер идет в самой функции.
        Ключ {markingMode: 'none'} Может, что ещё отключить, при компиляции? Я много чего перерыл, где-то упоминалось что на ios быстрее работает, чем на android. Типа дело в работе с движком V8, а в браузере всё давно вылизано, нативно, потому и летает.

        Там, правда функция локализации времени на java написано для android, я отключал мало чем помогло. Плагин intl - так себе.


  1. DamirMur
    27.12.2023 07:26

    Не подскажете, ссылку не дадите, как правильно билдить в прод?


    1. Bezlepkin Автор
      27.12.2023 07:26

      iOS/Android?


      1. DamirMur
        27.12.2023 07:26
        +1

        Желательно и то и другое


        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/
          Но можно настроить гораздо шире.


          1. DamirMur
            27.12.2023 07:26

            Спасибо, но там многое не описано. Куда например поместить значок приложения, куда картинку для экрана загрузки, как правильно вставлять экраны для рекламы. Нет ключей оптимизации (возможно они не нужны), но я где-то встречал и упустил, что если вставить какой-то ключ, то полностью отключаются все дебаги и программа работает быстрее.


  1. Shannon
    27.12.2023 07:26

    NativeScript выглядит интересно. А по поводу Framework7, есть способ отключить на ios жест тянуть вниз, как в браузере для перезагрузки страницы?


    1. Bezlepkin Автор
      27.12.2023 07:26

      Честно говоря я не знаю :) я с ним с пандемии не работал..


  1. DamirMur
    27.12.2023 07:26

    Команды NativeScript CLI https://fig.io/manual/ns


  1. DamirMur
    27.12.2023 07:26
    +1

    8 шагов для публикации вашего приложения NativeScript в магазинах приложений -https://blog.nativescript.org/steps-to-publish-your-nativescript-app-to-the-app-stores/