Можно ли обойтись без нативных языков, разрабатывая на Flutter и RN?

Разработка
2 ноября 2023

Привет! Меня зовут Дима, я разрабатываю мобильные приложения на Flutter в Пиробайт. Все чаще мне приходится сталкиваться с задачами, в которых требуется обращение к нативному API устройства. Пакетов на pub.dev для решения этих задач мне всегда хватает. Но бывают моменты, когда пакет работает не так, как надо, либо требует сложной настройки для работы.

Поэтому я стал заглядываться на изучение нативных языков, дабы уметь внедрять их в проект и не зависеть от других пользователей, которые пишут пакеты на Flutter (или хотя бы знать, как исправлять за ними ошибки в коде).

Передо мной встала дилемма — будет ли время, затраченное на изучение нативки, стоить того, чтобы избавиться от некоторых недостатков кроссплатформенной разработки? Или можно обойтись без этого? Ниже я расскажу, к какому выводу пришел за время своей работы.

Нужно ли знать нативные языки разработчикам на Flutter?

Для начала представьте, вам нужно написать сервис на Flutter, который следит за количеством заряда батареи смартфона. Одного нашего фреймворка для реализации фичи будет маловато, а из коробки такого инструментария нет. И представим, что никаких пакетов с таким функционалом тоже нет. Но Flutter может обратиться к функциям, написанным на нативном коде.

То есть мы пишем метод, возвращающий количество заряда батареи на 2-х платформах и вызываем эти методы уже внутри Flutter. Причем, если приложение запускается на Android, то будет вызван нативный код Android. Та же ситуация с iOS. Не нужно вручную обращаться к конкретным файлам, это и делает интеграцию нативного кода удобной.

Так выглядит часть кода на Kotlin

Так выглядит часть кода на Swift

Вот так Flutter вызывает выполнение нативного кода конкретной платформы

— Получается, без нативных языков никуда? — спросит кто-то.

— Ну а как иначе сделать отдельные функции вроде слежки за зарядкой, выбора файлов с устройства, доступу к камере и другим нативным фичам? — сказал бы я, да не все так просто.

Что на Flutter, что на React Native есть огромное количество пакетов, созданных пользователями. Нужен набор файлов? Уже написан пакет! Доступ к камере? Пожалуйста! Вход по биометрии? Легко! К какой бы нативной части устройства не нужен был доступ, пакет для него уже написан с вероятностью в 99%.

Количество пакетов для камеры на RN

У Flutter плюс минус такая же ситуация, пакетов предостаточно

— Стоп. Получается, знать нативные языки при разработке кроссплатформы все же не обязательно, если нативная часть уже написана пользователями?

— Тоже неверно.

Как раз таки тот факт, что все эти решения были написаны пользователями и делает знание нативных языков неплохим дополнением при разработке.

И вот почему.

Представьте, вы разрабатываете приложение и вам нужно подключить какой-либо пакет. Сканер QR-кодов, например. Вы его нашли. Приложение в релизе, все работает, пользователи довольны. Но создатель пакета в какой-то момент перестал его поддерживать, а фреймворк, на котором вы работаете, продолжает обновляться. И в один ужасный день его новая версия больше не поддерживает пакет, который долго не обновлялся.

— И что же делать, оставаться на старой версии фреймворка навсегда? Или вообще вырезать функцию сканера QR из приложения?

Вот тут то и пригождается знание нативных языков! Все пакеты, что для Flutter, что для React Native, имеют открытый исходный код, то есть любой пользователь может заглянуть "‎под капот‎"‎ и узнать, как этот пакет работает. Или создать его копию локально. Или модифицировать в Git так, как нужно.

То есть если автор перестал поддерживать пакет, то при знании нативных языков разработчик может продолжать его дальнейшую поддержку сам, внося туда те изменения, которые необходимы после обновления фреймворка.

Но такое происходит не каждый день — сообщество у обоих фреймворков огромное и шанс, что популярный пакет останется совсем без поддержки, крайне мал.

Даже если нужна какая-то кастомная логика, то можно взять существующий пакет и доработать его так, как требует бизнес-логика конкретного проекта. Но такое тоже происходит нечасто, а у некоторых разработчиков не происходит вообще. Хотя существуют редкие кейсы, когда необходимого пакета или плагина просто не найти. И как бы ни хотелось, придется написать его самому.

Пример — банковское приложение.

Чтобы оно работало, надо внедрить в него специальные сертификаты. Но высока вероятность, что специализированного для этой задачи пакета попросту не существует, потому что далеко не все команды разработчиков пишут приложения для банков.

Поэтому единственный выход — смотреть, как такие сертификаты внедряются на нативных платформах и внедрять их именно через нативную часть, вызывая написанный код уже на стороне кроссплатформенного фреймворка.

А еще не стоит забывать про тот факт, что Google и Apple часто обновляют свой софт, внедряя в него новые функции и возможности для пользователей (тот же FaceID на iOS).

При добавлении нового функционала в свою ОС, компании предоставляют инструментарий для их внедрения в средства для разработки нативного приложения. И разработчики нативки получат эти инструменты чуть ли не на следующий день, после обновления ОС.

А вот кроссплатформе остается 2 варианта:

1) Либо ждать, пока какой-нибудь энтузиаст напишет плагин, с помощью которого можно будет использовать эти новые функции (и то не факт, что он сделает это качественно);

2) Либо самому написать нативную часть и пользоваться ею.

Конечно, со временем все пакеты и плагины будут сделаны, но если бизнес хочет внедрить все новые фичи как можно быстрее, то тут без написания натива не обойтись.

Теперь вообще ничего не понятно — надо знать нативные языки, разрабатывая на Flutter или React Native, или не надо?

Лично я пришел к тому, что нет. Не обязательно. Важны не столько знания нативных языков, сколько знания нюансов нативных платформ. Это дает понимание о том, как работает платформа, какие у нее отличия от конкурента, в чем ее сильные и слабые стороны.

К примеру, у Android на момент написания этой статьи есть 14 версий API и 34 версии SDK. Немало, согласитесь? И было бы неплохо, если бы разработчик мобильного приложения на кроссплатформенном фреймворке знал, что́ поддерживает или не поддерживает эта API или SDK.

Пример 1

Представьте, что на проект подключается пакет для работы с нативным API. Проект поддерживает все API Android выше 8 версии. И разработчик, знакомый со спецификой платформы, может знать, что, например, эти функции работают с некоторыми ограничениями на конкретных версиях Android. А потому сразу может заложить решения, которые помогут пользователям с устройствами не видеть постоянных вылетов и багов на таком софте.

Пример 2

Или возьмем пользовательский опыт: на Android есть системная кнопка «назад», которая позволяет перейти на предыдущий экран или выйти из приложения. Заказчик требует при нажатии на эту кнопку сделать какую-нибудь проверку и, например, перенаправить пользователя не на предыдущий экран, а на другой. Всё работает, все рады. Однако на этапе тестирования на iOS вспоминается, что на айфонах такой кнопки нет. Там навигация назад реализована с помощью жестов (причем не просто быстрого свайпа, а пользователь может на половине жеста передумать и остаться на этом же экране).

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

Пуш-уведомления, работа в свернутом виде, splash-экран, доступ к хранилищу на устройстве и многое другое — все эти вещи похожи на обеих платформах. Но у них есть и различия, которые нужно знать мобильному разработчику на кроссплатформе, чтобы не наткнуться на неприятные сюрпризы при тестирования или, не дай бог, уже после деплоя приложений в сторы.

Подведу итог

Знать нативные языки при разработке кроссплатформенных приложений не обязательно, но будет большим преимуществом. Как для разработки, так и для своего развития. Первое — это позволяет не зависеть от решений, созданных другими разработчиками. Второе — здорово расширяет собственные возможности.