Хабрахабр

Приручить зверя. С чем мы столкнулись при разработке приложения для ведения личного дневника на React Native

В предыдущей статье я подробно рассказал о нашем опыте создания веб-сервиса/мобильного приложения для ведения личного дневника. Актуальная версия приложения (минимальная работоспособная версия уже выложена в Google Play) разрабатывается на React Native, и вот на нем мы и остановимся подробно сегодня.

Рассказываем о собственном опыте работы с фреймворком, способах расширения функционала, «подводных камнях» (куда ж без них!) и как мы их обошли.

О фреймворке в целом

Немного о виновнике торжества — React Native. Он все-таки хорош!

Если же есть опыт с React, ну или хотя бы есть понимание ее идеи, механизма — он просто великолепен! Для тех, кто в достаточной степени знает JavaScript и тем более NodeJS — он очень хорош.

Расширения и плагины покрывают практически 99% типовых задач. Главное, что на выходе получается действительно нативное приложение. Оставшийся процент при острой необходимости можно дописать на родных языках (java, object-c) и подключить к React Native приложению.

Все плюшки и вкусности бессмысленны, если приложение не запускается, а это первое чем нас «порадовал» React Native. Но хватит про плюсы, от них толку ноль, хоть список и будет внушительным.

Потом версия npm. Сначала ему не понравилась версия NodeJS. Потом версия Android SDK, потом версия Android tools, потом… Писать про то, как все проблемы решились, смысла нет, ибо с того момента все вышеперечисленное ПО обновило свои версии и инструкции будут неактуальны.

Будьте готовы к штудированию google, чтению форумов и stackoverflow. Просто знайте: узкое место React Native — среда сборки. 6 — 3 дня, Win10 — 2 дня. На развертывание в итоге потратили: Ubuntu 12. Как ни странно, на «винде» все оказалось проще, ну, или просто на ubuntu «шишек набили» и уже понимали, что и куда подсовывать.

На заметку, вдруг кому пригодится: код, представленный ниже, решил все проблемы с совместимостью версий sdk у дополнений при компиляции проекта.

subprojects } }
}

Прописывается в файле /android/build.gradle в самом конце. Без этой «директивы», судя по всему, каждый из плагинов/расширений пытался компилироваться по своим собственным версиям Android SDK, что приводило сборку проекта в хаотичное ассорти из лютых ошибок и богомерзких предупреждений. Никто не знает, насколько актуально будет рекомендация в будущем. Но на сегодняшний день, особенно после того как Google принудительно запретил для компиляции использовать SDK ниже 26-й ревизии, это очень даже помогает.

В репозиториях куча нерешенных issues. Второе «узкое» место — боль не столько React Native, сколько, видимо, всего Open Source в целом — ограниченная поддержка. Никто никому ничем не обязан. Лютые «умные» боты закрывают баги при отсутствии активности иногда аж через 7 дней… И вроде все это нормально. Все привыкли.

about.me text input

Просто текстовое поле. Терпение лопнуло, когда обнаружился «косяк» при банальном вводе текста в обычный TextInput. Через пару минут печатания начинается жутчайшие лаги и тормоза системы. Просто ввод текста с экранной клавиатуры. Нет. Бросились искать проблему — да, есть такое, началось с версии RN 5x.xx Проблему решают? Еще несколько слиты в один большой. Два или три issues по теме просто закрыто.

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

База данных

Realm — шустрая база данных, с внушительным функционалом и работающая на Android, IOS, Windows.

Непривычно и странно, особенно для веб-разработчика родом из PHP, выросшего на ActiveRecord и Doctrine. Поначалу было двоякое ощущение, мол, никакой тебе ORM, реально нет sql, запись ведется только внутри callback. А все вопросы вкусовщины и привычек разрешились чтением официальной справки, краткой, лаконичной и понятной. Но по факту набросать свой минимальный набор функций для CRUD оказалось совсем просто и быстро.

А потом и вовсе началась карусель подарков:

  • Шифрование данных, из коробки
  • Ленивая загрузка данных (тянет из базы только то, что нужно прямо сейчас)
  • Реальные связи между сущностями (привет, mongo!
  • Версионирование структуры БД, с миграциями — из коробки
  • И еще куча маленьких, но приятных мелочей.

about.me index search
Казалось, вопрос с БД закрыт совсем. Работаем! Дело спорилось, пока не дошли до поиска. Вернее, до полнотекстового поиска. Еще точнее, до полнотекстового поиска на русском языке без учета регистра. Он не работал. Совсем. На английском — работал. С учетом регистра тоже работал. А вот без регистра, да на русском — хоть плачь. Покопав справку, багтрекер и интернет, выяснилось что разработчику в силу определенных технических причин было очень неудобно «думать» о поддержке мультибайтовых кодировок и всего, что выходит за рамки латиницы. Ну вот он и не стал. А почему бы и нет?

В результате непродолжительного штурма, было принято «волевое» решение — делаем отдельное поле «fulltext_index». Делать нечего, пришлось искать обходное решение. После этого, логично предположить, делаем поиск с принудительным верхним регистром. В него дублируем весь текст в верхнем регистре, попутно «выпиливая» ненужные знаки препинания, лишние проблемы и разного рода мусор.

Поиск теперь работает как часы хоть на русском, хоть на английском! Победа!

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

Навигация по экранам

wix/react-native-navigation — простой и стабильно работающий навигатор (роутер, как сказал бы веб-программист).

В общем, минимальный нужный минимум. Был выбран только потому, что прошел все нужные внутренние тесты (открытие экрана, стек вызовов, возврат, сайдбар).

Так оно и есть — все переходы между экранами транслируются в java код приложения и отрабатывают на уровне системы. about.me wix slider
В отличие от широко любимого всеми react-navigation у wix заявлена 100% нативность.

Случается, при выходе из «спящего» режима, процесс загрузки просто замирает. В процессе разработки столкнулись с жутким багом «белого экрана», возникающего только в некоторых случаях и на отдельных устройствах. На github по данной проблеме нашлись лишь странные намеки на «...try to play» с очередностью загрузки экранов и прочая колдовская благодать. Дебаггер и отладка молчат. После того, как мы потанцевали с бубном, ошибка стала проявляться реже, но совсем не ушла, зависнув в списке нерешенных задач. Толком даже не понятно, на каком уровне проблема зарыта: java-код андроида или уже в машине JavaScript. Увы.

А, главное, нативно! За вычетом данного «косяка» — все более-менее сносно и гладко.

Файловая система

От файловой системы нам нужно было хранение пользовательских фото, а также работа с парой файлов, связанными с резервным копированием. В результате выбора из двух возможных вариантов выбор пал на react-native-fs
about.me wix slider
«Доступ к нативной файловой системе» — написано на входе в репозиторий. Что ж, наверное, так и есть, но с некоторыми поправками и ограничениями.

Доступ только асинхронный. 1. Хотя в React об этом начинаешь забывать. В результате иногда приходится вспоминать работу с Promise / async / await.

Для этого достаточно просто добавить async перед именем функции. Синхронное выполнение асинхронной функции (await), требует чтобы текущая функция была Асинхронной (async). Component это работает тоже. И да, для метода класса React. (в справке React, ReactNative об этом умалчивают, хотя это само собой подразумевается).

export default class CloudIndex extends BasePage { async setupBackupFolders(init = false) { // some stuff there... await RunSomeAsyncFuncInSyncMode(foo, bar) RunFuncAfter(bar) }
}

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

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

В копилку маст хев. А в целом, свои задачи решает на 100%.

Доступ к облаку

Задача одновременно простая и сложная. Простая, потому что у всех есть API — бери и пользуйся. Сложная — лезть в глубины не хочется, да и формат времени не позволял сидеть и ковыряться в «возможно работающих» способах. Решили найти то, что работает 100% и реализовано в уже готовом расширении для React Native.

Работа с диском понятна и рулится банальными запросами на API. Таких нашлось ровно одно: Google Drive. А вот получение доступа приложения к диску — совсем другая история.
React-native-google-signin — система управления авторизацией в сервисах гугла.

Хотели, что попроще и понадежней, а получили… about.me wix slider
Вот здесь-то мы и «повеселились».

Раньше всем этим занимался сам Google. Все началось с получения ключа разработчика. Но после поглощения FireBase было решено перенести эту функцию в ее чудесную консоль.

Итак, чтобы получить ключ, нужно:

  1. Зарегистрировать приложение на google developer console чтобы там «включить» доступ к Drive службе.
  2. Зарегистрировать приложение на firebase console.
  3. Сформировать в firebase console файл google-services.json — в котором зашиты ключи сервиса.
  4. Подсунуть этот файлик в проект с установленным расширением react-native-google-signin.

И тогда, да. Что-то начинает работать. Вернее, коды ошибок в ответах сервиса начинают быть осмысленными.

Иногда он меняется раз в день, иногда раз в минуту. Особенно важно отметить, что API key, получаемый приложением непосредственно при подключении к сервису, совсем не вечный. А если он просрочен — получать заново. Поэтому, перед обращением к сервису всегда сначала лучше проверять, не просрочен ли текущий ключ.

Процесс получения API ключа у Google выглядит следующим образом:

await GoogleSignin.hasPlayServices()
const userInfo = await GoogleSignin.signIn()
this.setState({ userInfo: userInfo,
})
settings.set('google.drive.key', userInfo.accessToken)
trace('>> Key obtained:', userInfo.accessToken)
this.apiKey = userInfo.accessToken

Так, например, в нашем приложении, при открытии экрана бекапа мы пытаемся получить у Google id папки с бекапами. Если все успешно — мы получаем id.

backupRootID = await Storage.safeCreateFolder({ name: backupFolder, parents: ["root"],
}).catch((e)=>{ if(e.status == 401) { trace(' >> Google signin unauthorized', e) signGoogle() return false } else { trace(' >> Google signin failed', e) }
}) // Yeahh. The api key is valid, and rootID found on GoogleDrive!
SomeStorage.setRootId(backupRootID)

Если нет (пришла 401 ошибка) — пытаемся получить новый API ключ и повторяем запрос на получение id папки с бэкапом заново.

И еще несколько приятных мелочей

Работа с датами и временем

Честь и хвала moment.js
Знакомство с этим чудом началась уже давным-давно и было чертовски приятно, что он так же хорошо работает и в среде React Native.

Поддержка многоязычности и национальных форматов. Куча форматов, магические + — день / месяц / год. Красота!

Можно закидать нас помидорами, указав, что все это легко «рулится» руками с обычными Date, но в условиях быстрой разработки НЕ думать о таких вещах очень и очень полезно!

Графики и диаграммы

about.me wix slider
React-native-charts-wrapper — обертка на JavaScript для родного андроидного MPAndroidCharts.

Понравилось наличие обилия различных типов графиков (хотя на данным этапе мы использовали только два из них — линейный и «пирог»).

Автор рекомендует смотреть документацию по оригинальному MPAndroidCharts. Подпортил впечатление скудный почти отсутствующий справочник API. Кроме того, MPAndroidCharts написан на Java. По факту, совет оказался трудновыполнимым, так как разработка последнего ведется непрерывно и на несколько версий обгоняет реализацию враппера. Быстро сообразить что к чему бывает сложно, приходится задумываться. Враппер – на JavaScript.

Мультиязычность и переводы

about.me wix slider
React-native-i18n -work like a charm, guys!

Все переводы аккуратно раскиданы по файлам с языками. Хоть данный компонент и висит на github с пометкой Deprecated, но работает он без сбоев и косяков.

Использование параметров транслятора работает тоже на ура:

// en.js
sync: { success: 'All items are up to date!', progress: 'Sync Notes %{idx} of %{total}'
} //app.js
import I18n from 'react-native-i18n'
import en from './en.js' I18n.translations = { en }
I18n.locale = "en" const _t = (msg, data) => { return I18n.t(msg, data) } console.log(_t('sync.progress', {idx: 3, total: 10}))

В сухом остатке

React Native оправдал практически все свои ожидания. С его помощью можно относительно быстро собрать прототип приложения, отработать структуру и юзабилити. Все необходимые инструменты для «базы» есть.

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

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть