Як ми інтегрували React Native у наявний Android застосунок. Розглядаємо реальний кейс
Всім привіт 👋 Мене звати Андрій, я Engineering Manager та Android Engineer в компанії Uptech. Вже понад 5 років я створюю Android-застосунки, щоб заощаджувати час людей та підвищувати фінансові показники різних продуктів. Мені часто доводиться стикатись з чимось незвичним для Android-інженерів.
У цій статті я розповім, які рішення ми приймали і як вирішували технічні проблеми в інтеграції React Native в наявний Android-застосунок та як такий підхід оптимізував бізнес-процеси. Також поговоримо про те, як змінились процеси всієї команди після інтеграції.
Більшість статей, які я читав про React Native, пропонують схожий підхід: створити монорепозиторій з конфігураціями за замовчуванням і далі вже працювати над основною функціональністю застосунку.
А якщо, наприклад, потрібно реалізувати частину мобільного застосунку нативною мовою, а частину на React Native? Може здатися, що це жарт із попсового серіалу про програмістів. Втім, це реальний кейс із практики нашої команди. Дуже часто бізнес говорить фінансовою мовою, і якщо є хоча б якась технічна можливість покращити ефективність та швидкість розробки — бізнес використовує її, і це абсолютно логічно.
В яких випадках може знадобитися підхід інтеграції React Native в наявний Android-застосунок:
- ви хочете пришвидшити розробку Android, iOS та Web, проте у вас вже є застосунки, і вам необхідно робити перехід на нову технологію більш плавно;
- у вашій команді з’явились сильні React Native спеціалісти, які можуть забезпечити стабільність нової функціональності.
Історія одного розробника...
Одного дня наш клієнт вирішив провалідувати гіпотезу інтеграції React Native в наявний застосунок. Як Android-девелоперам нам необхідно було не писати код на JS, а просто виконати інтеграцію в наявний застосунок. Недовго думаючи, ми висвітлили клієнту всі технічні та продуктові ризики, які може спричинити інтеграція, та пройшли всі етапи моделі Кюблера-Росса.
Спочатку ми повністю заперечували цей підхід, адже він має багато недоліків: ускладнення деплоймент-процесів, ускладнення підтримки кодової бази тощо. Потім команда гнівалася. Після цього ми перейшли до торгів і надавали експертну оцінку, де зважували всі «за» та «проти» обраного підходу. Зрозумівши, що є шанс, що короткостроково це може бути фінансово невигідним для продукту, проте довгостроково може бути виграш, команда прийняла та почала інтеграцію.
Метою експерименту було визначити, наскільки можна покращити фінансові показники, адже за такого підходу розробка велась би в одній кодовій базі. Це означає, що необхідно мати одного розробника (React Native) замість трьох (Android, iOS, Web) і кожна продуктова зміна велась би лише один раз замість трьох. Перевірка мала відбуватись на одній з частин застосунку, а саме на Signup Flow.
Ну що ж, тепер, коли ми зрозуміли, що такий кейс має місце в реальному житті, розгляньмо технічні проблеми, з якими ми зіштовхнулися, і як їх вирішували.
Проблема № 1. Керування репозиторіями
Звичайний підхід, який ви можете зустріти в одному з мільйонів гайдів з розробки React Native застосунку — це створення монорепозиторія. Логічно це означає, що розробка над Android, iOS та Web відбувається в одному репозиторії.
Це абсолютно нормально при розробці мобільних застосунків за допомогою фреймворка React Native, оскільки весь застосунок зазвичай пишеться мовою JavaScript/TypeScript та більшість пул-реквестів стосуватимуться лише однієї кодової бази.
Проте якщо потрібно вести Android-розробку паралельно React Native розробці, матимемо наступні проблеми:
- список пул-реквестів завжди буде переповненим, це додасть складності в процес розробки;
- будь-яка зміна в конфігурації буде впливати на розробку всіх платформ, особливо під час критичних релізів;
- перенесення наявного застосунку в інший репозиторій — це тривалий процес, що потребує зупинки роботи всіх розробників над поточними задачами. Також це може призвести до видалення git історії;
- монорепозиторій міститиме повноцінні нативні застосунки та лише частину функціональності у вигляді React Native.
Вирішення проблеми № 1
Звичайно, потрібно тримати репозиторії мобільних застосунків окремо від React Native коду. При цьому рекомендую робити React Native репозиторій як монорепо, оскільки можна буде тестувати окрему частину функціональності, що написана на React Native, в режимі standalone. У такому випадку ми не матимемо тих проблем, які б мали при створенні монорепозиторія для всіх платформ. Як це можна реалізувати? Є два варіанти, які працюють фактично однаково:
- Робити імпорт репозиторія з React Native кодом, як підмодуль (git submodule). Це потужний механізм, який може зробити зв’язку двох репозиторіїв чистішою.
- Мануально клонувати репозиторій в директорію з Android-проєктом, або ж будь-яку іншу директорію. Головне, щоб репозиторій клонувався стабільно в ту саму директорію, оскільки потрібно буде правильно встановити зв’язок в gradle файлах.
Проблема № 2. Генерація релізних та демонстраційних білдів
Мені, як Android-розробнику, було незрозуміло, де розташований JS код та як я можу його додати в кінцевий .aab/.apk файл. Відповідь виявилась очевидною: файл index.bundle, який можна згенерувати за допомогою звичайної команди в React Native репозиторії:
$ react-native bundle --platform android --dev false --entry-file packages/signup/index.js --bundle-output path-to-assets/assets/index.android.bundle --assets-dest path-to-app/src/main/res/"
Ця команда створить index.bundle файл та запише його в директорію assets з вашим Android-проєктом. Також, якщо React Native містить будь-які файли ресурсів (картинки, мелодії, тощо), він автоматично запише їх у папку res.
Врахуйте, що path-to-assets та path-to-app будуть різні для кожної конфігурації та залежатимуть від того, де розташований React Native проєкт (підмодуль, клонований у папку з визначеним розробником шляхом).
Проте, як ви розумієте, проблема не в тому, щоб створити файл index.bundle. Проблема з’являється тоді, коли вам необхідно згенерувати різні .aab/.apk файли для різних середовищ, на кшталт production та staging.
Вирішення проблеми № 2
По-перше, ваш Android-проєкт повинен містити вже сконфігуровані build variants. По-друге, частина застосунку, що написана на React Native, повинна містити будь-який механізм роботи з різними типами середовищ. Ми, наприклад, використовували бібліотеку react-native-dotenv для конфігурації середовищ. Усе, що потрібно було змінювати — лише .env файл, що розташований у корені React Native проєкту.
Щоб кожного разу не виписувати конфіденційні дані (токени, ключі) в .env файл вручну, був створений react_env_generator.sh скрипт, що брав необхідні ключі з .gradle/gradle.properties файлу (або ж environment variables сховища на СІ) та переносив їх в .env файл у необхідному для React Native частини застосунку форматі.
Приклади скриптів ви можете знайти в наступних GitHub gist-ах.
- Генератор змінних середовища, що отримуються локально з gradle.properties файлу
- Генератор змінних середовища, що отримуються з змінних СІ
Тепер ми знаємо, що середовище React Native частини залежить лише від .env файлу, тому щоб зробити два різних .aab/.apk файли (production та staging), нам потрібно лише:
- Згенерувати .env з production ключами генератором, після чого створити index.bundle файл командою, описаною вище.
- Згенерувати .env з staging ключами генератором, після чого створити index.bundle файл командою, описаною вище.
І тут настає кульмінація. Якщо ви робитимете ці команди підряд, швидше за все у вас буде два однакових index.bundle файли з однаковими конфіденційними ключами, а це значить, що .env файл не змінився.
Ви хочете запитати, чому? Відповідь дуже неочевидна. React Native фреймворк робить кешування .env файлів при послідовній генерації бандлів і, щоб запобігти цьому, нам потрібно додати прапор reset-cache в команду генерації index.bundle файлу. У результаті маємо:
$ react-native bundle --platform android --dev false --reset-cache --entry-file packages/signup/index.js --bundle-output path-to-assets/assets/index.android.bundle --assets-dest path-to-app/src/main/res/"
На жаль, на просторах інтернету я не стикався з цим прапором, як результат, дійшов до нього емпіричним шляхом.
Проблема № 3. Навігація
Є безліч способів організації навігації в межах React Native застосунку. Серед них: React Navigation, React Native navigation від Wix, React Native Router Flux та багато інших. Було б круто, якби React Native розробники доповнили цей список у коментарях з описом підходів, які їм найбільше подобаються для різних випадків.
Проте не забуваймо, що ми говоримо про випадок, коли в нас лише частина застосунку написана на React Native, а решта написана нативною мовою програмування. І навігацію між React Native та Native частиною трошки складніше організувати, особливо коли необхідно мати механізм передачі даних. Якщо React Native розробник не дуже досвідчений у нативній розробці, бізнесу необхідно буде залучити нативних розробників (що збільшує виробничі витрати), адже потрібно створити міст між React Native кодом і нативним у Android-застосунку.
Вирішення проблеми № 3
Щоб коректно побудувати навігацію між нативною частиною застосунку та React Native стороною, необхідно побудувати міст, про деталі реалізації та тестування якого ви можете почитати в офіційній документації на reactnative.dev. Я постараюсь коротко описати основні компоненти, які необхідно додати для побудови навігаційного моста.
Android
Перш за все я рекомендую розбити застосунок на модулі. Якщо у вас застосунок уже містить модулі, то створіть окремий для запуску частини React Native. Якщо ж застосунок не розбитий на модулі — створіть 2 модулі: native-application та react-native-flow. Це дозволить вашій gradle системі не робити заново білд усіх залежностей react native у випадку, коли ви будете робити зміни у нативній частині коду.
- Ствоюємо клас, що наслідується від ReactContextBaseJavaModule та створюємо методи, які ми будемо викликати з ReactNative сторони. Не рекомендується змінювати назви методів, оскільки в такому випадку доведеться це змінювати і в React Native репозиторії:
- Створюємо package клас, що наслідує ReactPackage зі стандартної бібліотеки ReactNative, де створюємо екземпляр класу, описаного в попередньому пункті:
- При створенні екземпляру react application передаємо наш package, що створили в попередньому пункті:
class NativeNavigationModule constructor(private val navigator: ReactNativeSignupNavigation, reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { override fun getName(): String = "NativeNavigation" @ReactMethod fun navigateToLoginScreen(email: String?) { navigator.logoutAndNavigateToLoginScreen(currentActivity, email) } @ReactMethod fun navigateToWelcomeScreen() { navigator.logoutAndNavigateToWelcomeScreen(currentActivity) } }У прикладі вище ви бачите об’єкт navigator: ReactNativeSignupNavigation. Це клас, що надає нам Dagger (чи будь-який інший контейнер залежностей) в наш react-native-flow модуль. Його реалізація розташована в модулі app, оскільки лише в цьому модулі є доступ до екранів з native-application модуля.
interface ReactNativeSignupNavigation { fun logoutAndNavigateToLoginScreen(currentActivity: Activity?, email: String?) fun logoutAndNavigateToWelcomeScreen(currentActivity: Activity?) }
class NativeNavigationPackage constructor(private val navigator: ReactNativeSignupNavigation): ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> = listOf(NativeNavigationModule(navigator, reactContext)) override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<View, ReactShadowNode<*>>> = Collections.emptyList() }
val reactInstanceBuilder = ReactInstanceManager.builder() .setApplication(application) .setCurrentActivity(this) .setJSMainModulePath("packages/signup/index.js") .addPackage(MainReactPackage()) ... .addPackage(NativeNavigationPackage(reactSignupNavigator)) .setUseDeveloperSupport(BuildConfig.DEBUG) .setInitialLifecycleState(LifecycleState.RESUMED)
React Native
Сворюємо файл (наприклад NativeNavigation.js), який буде отримувати методи, створені в нативному коді:
import { NativeModules } from 'react-native'; export default NativeModules.NativeNavigation;
Далі в тому місці, де необхідно зробити навігацію, просто викликаємо наш створений метод:
import NativeNavigation from '../../services/Native/NativeNavigation'; ... NativeNavigation.navigateToLoginScreen(user.email); ...
Ось і все, тепер ми успішно зробимо навігацію з React Native частини в Native.
Якщо ж нам потрібно зробити навігацію навпаки — з нативної частини в React Native, то необхідно запустити Activity, що наслідується від ReactActivity, з необхідними параметрами, які визначатимуть поточний стан ReactNative частини застосунку:
class SomeReactNativeScreen : ReactActivity() { private var reactInstanceManager: ReactInstanceManager? = null @Inject lateinit var reactNavigator: ReactNativeSignupNavigation override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) AppInjector.provideReactSignupComponent<ReactSignupComponent>()?.inject(this) setupReactInstance() val targetScreenEntryPoint = intent?.getStringExtra(TARGET_SCREEN_ENTRY_POINT) ?: "" setContentView(getReactRootView(targetScreenEntryPoint)) } private fun setupReactInstance() { val reactInstanceBuilder = ReactInstanceManager.builder() .setApplication(application) .setCurrentActivity(this) .setJSMainModulePath("packages/signup/index.js") .addPackage(MainReactPackage()) .addPackage(NativeNavigationPackage(reactNavigator)) .setUseDeveloperSupport(BuildConfig.DEBUG) .setInitialLifecycleState(LifecycleState.RESUMED) if (BuildConfig.BUILD_TYPE != "debug") { reactInstanceBuilder.setBundleAssetName("index.android.bundle") reactInstanceBuilder.setJSBundleFile("assets://index.android.bundle") } reactInstanceManager = reactInstanceBuilder.build() } private fun getReactRootView(targetScreenEntryPoint: String): ReactRootView { val result = ReactRootView(this) result.startReactApplication(reactInstanceManager, "signup", Bundle().apply { putString("ENTRY_POINT", targetScreenEntryPoint) putString("SOME_DATA", "value") }) return result } companion object { const val TARGET_SCREEN_ENTRY_POINT = "SomeReactNativeScreen.TargetScreenEntryPoint" } }
Проблема № 4. Continuous Integration
Нині в світі без автоматизації ніяк, саме тому весь проєкт разом із React Native частиною необхідно якось запустити на CI. Оскільки ми використовуємо CircleCI, приклади наводитиму саме для цієї СІ, проте не думаю, що буде проблемно змінити конфігурацію на будь-яку іншу СІ. Всі jobs я не описуватиму, лише основні.
Варто уточнити, що ми не використовуємо підмодулі, а напряму клонуємо React Native репозиторій у кореневу папку з Android-проєктом. Чому саме так? Просто тому, що так склалось на проєкті в умовах швидкої інтеграції. Ви можете робити так само або з використанням підмодулів, різниця буде несуттєвою.
Вирішення проблеми № 4
- Build react native job:
- Deploy stage build to Firebase App Distribution (ми завантажуємо білди за допомогою Fastlane):
- Release app into Google Play store
build-react-native: <<: *container_config steps: - checkout - run: name: Clone React Native repository command: git clone [email protected]:YOUR_PROJECT/YOUR_REACT_NATIVE_REPOSITORY.git ~/YOUR_PROJECT_FOLDER_NAME - restore_cache: key: yarn-v1-{{ checksum "~/YOUR_PROJECT_FOLDER_NAME/yarn.lock" }}-{{ arch }} - restore_cache: key: node-v1-{{ checksum "~/YOUR_PROJECT_FOLDER_NAME/package.json" }}-{{ arch }} - run: command: "cd ~/YOUR_PROJECT_FOLDER_NAME && yarn install" name: "Install react native dependencies" - save_cache: key: yarn-v1-{{ checksum "~/YOUR_PROJECT_FOLDER_NAME/yarn.lock" }}-{{ arch }} paths: - ~/.cache/yarn - save_cache: key: node-v1-{{ checksum "~/YOUR_PROJECT_FOLDER_NAME/package.json" }}-{{ arch }} paths: - ~/YOUR_PROJECT_FOLDER_NAME/node_modules - run: name: Create folders for react native bundles command: | mkdir -p ~/tmp/stageBundle mkdir -p ~/tmp/prodBundle sudo chmod u+x ./scripts/react_env_generator.sh - run: name: Generate react Staging env variables command: | ./scripts/react_env_generator.sh stage - run: name: Make react native stage bundle command: "cd ~/YOUR_PROJECT_FOLDER_NAME/packages/signup && yarn android-ci-stage-bundle" - run: name: Clean React Native cache command: | cd ~/YOUR_PROJECT_FOLDER_NAME rm -rf $TMPDIR/metro-cache rm -rf $TMPDIR/react-native-packager-cache-* rm -rf .env rm -rf packages/signup-web/.env yarn cache clean - run: name: Generate react Prod env variables command: | ~/code/scripts/react_env_generator.sh prod - run: name: Make react native prod bundle command: "cd ~/YOUR_PROJECT_FOLDER_NAME/packages/signup && yarn android-ci-prod-bundle" - persist_to_workspace: root: *workspace_root paths: - YOUR_PROJECT_FOLDER_NAME - tmp - code
stage: <<: *container_config steps: - *attach_workspace - run: yes | sdkmanager --licenses || exit 0 - run: yes | sdkmanager --update || exit 0 - run: command: gem install bundler && bundle install && bundle update fastlane name: configure build - restore_cache: <<: *general_cache_key - run: name: Use stage react native bundle command: | rm -rf ~/code/App/app/src/main/assets/ mkdir -p ~/code/App/app/src/main/assets/ cp ~/tmp/stageBundle/index.android.bundle ~/code/App/app/src/main/assets/ - run: name: "Install Firebase CLI" command: | curl -sL firebase.tools | bash - run: name: "Deploy stage build to Firebase" no_output_timeout: 30m command: | cd App/ && bundle exec fastlane stage firebase_token:$FIREBASE_TOKEN - store_artifacts: path: ~/code/App/app/build/outputs/apk/debug/stage/app-stage-debug.apk
deploy: <<: *container_config steps: - *attach_workspace - run: yes | sdkmanager --licenses || exit 0 - run: yes | sdkmanager --update || exit 0 - run: command: gem install bundler && bundle install && bundle update fastlane name: configure build - restore_cache: <<: *general_cache_key - run: name: Use prod react native bundle command: | rm -rf ~/code/App/app/src/main/assets/ mkdir -p ~/code/App/app/src/main/assets/ cp ~/tmp/prodBundle/index.android.bundle ~/code/App/app/src/main/assets/ - run: name: "Generate signed aab prod bundle" command: | cd App/ && bundle exec fastlane sign_aab_lane flavor:Prod type:Release - run: name: "Fastlane deploy" no_output_timeout: 30m command: | cd App/ && bundle exec fastlane deploy_aab path:app/build/outputs/bundle/prodRelease/app-prod-release.aab mapping:app/build/outputs/mapping/prodRelease/mapping.txt
Зміни у процесах команди і продукту в цілому
Комунікація
До інтеграції React Native в нативні застосунки ми мали окремі команди Android, iOS та Web девелоперів. Якщо візуалізувати, то наша проєктна команда виглядала приблизно так:
Після інтеграції наша проєктна команда стала виглядати приблизно так:
Тому тепер наша Android-частина команди стала більше комунікувати з Frontend та iOS інженерами, оскільки часто потрібно узгоджувати поведінку застосунку на всіх платформах. Також, у випадку з побудовою мостів комунікації, нам потрібно збиратись усім разом і думати, як правильно будувати навігацію між різними частинами застосунку. В будь-якому випадку комунікація між департаментами покращилась.
Git
Щодо змін у Git процесах, після інтеграції React Native в нативний застосунок потрібно розуміти, що тепер у вас не один репозиторій, а декілька. Кожна зміна в React Native іншими розробниками може впливати на поведінку Android-застосунку. Саме тому необхідно, щоб React Native розробники тримали принаймні одну вітку стабільною. Це можуть бути як декілька різних віток для різних застосунків (Android/iOS), так і різні вітки для різних flow (signup/login). Нам, як Android-інженерам, необхідно слідкувати за стабільними вітками, особливо перед релізами. В іншому разі в один прекрасний день ви не зможете збілдити ваш проєкт, вже не кажучи про те, що частина функціональності не працюватиме.
Release management
Release management після інтеграції повинен включати комунікацію нативних розробників з React Native задля забезпечення наявності свіжих змін у всіх частинах застосунку. Перед релізом необхідно зібрати номери всіх тікетів (нативних та React Native) задля генерації свіжих release notes.
Issue tracking
Перед інтеграцією необхідно чітко визначити, де будуть розташовані тікети мобільних розробників, а де будуть розташовані тікети React Native. Дуже часто виникає ситуація, коли потрібно на React Native частині виправити помилку, яка існує лише на Android (наприклад, навігація назад за допомогою фізичної кнопки «назад»). У таких випадках не зрозуміло, на якій дошці повинен бути тікет, і лише чітке розмежування з самого початку інтеграції дозволить вам вирішити такі питання.
Також, якщо ви рахуєте velocity команди, необхідно визначити з самого початку як її рахувати для тих розробників, які працюватимуть над інтеграцією React Native. Звичайно, все залежить від проєкту, але один із варіантів трекінгу задач для React Native частини — це створення окремої дошки для React Native задач. У такому випадку вся робота, яка відбуватиметься на React Native частині, буде відслідковуватися на окремій дошці, що спрощує velocity calculation.
Onboarding
Нові розробники не повинні бути Альбертами Ейнштейнами для того, щоб увійти в курс справ проєкту. Навіть з такою дивною технічною інтеграцією процес онбордингу має бути суперпростим. Для цього я рекомендую створити набір команд, який робитиме весь процес налаштування React Native репозиторія на машинах розробників автоматично. Ось приклад одного з таких налаштувань:
fun Project.setupReactNativeRepository() { runCommand(""" rm -rf ../../../RELATIVE_PATH_TO_REACT_NATIVE_PROJECT mkdir ../../../RELATIVE_PATH_TO_REACT_NATIVE_PROJECT git clone https://github.com/YOUR_PROJECT/YOUR_REACT_NATIVE_REPOSITORY.git ../../../RELATIVE_PATH_TO_REACT_NATIVE_PROJECT chmod a+x ../../scripts/react_env_generator.sh ${getEnvGeneratorCommand()} rm -rf src/main/assets mkdir src/main/assets cd ../../../RELATIVE_PATH_TO_REACT_NATIVE_PROJECT && yarn clean ${getAndroidBundleCommand("../../../RELATIVE_PATH_TO_REACT_NATIVE_PROJECT/packages/signup")} """) } fun Project.getEnvGeneratorCommand(): String { return if (gradle.startParameter.taskNames.any { it.contains("Prod", true) }) { "../../scripts/react_env_generator.sh prod" } else { "../../scripts/react_env_generator.sh stage" } } fun Project.getAndroidBundleCommand(path: String): String { println(gradle.startParameter.taskNames) return if (!gradle.startParameter.taskNames.any { it.contains("Debug", true) }) { "cd $path && yarn android-bundle" } else { "echo \"To test react native flow you should run metro bundler by your own\"" } } // a wrapper closure around executing a string // can take either a string or a list of strings (for arguments with \n) fun Project.runCommand(strList: String) { val lines = strList.split("\n") lines.forEach { if (it.isNotBlank()) { try { project.exec { commandLine("bash", "-c", it.trim()) } } catch (e: org.gradle.process.internal.ExecException) { } } }
Виклик даного методу відбувається в build.gradle.kts вашого react-native модуля відразу після опису плагінів:
try { apply("../../../RELATIVE_PATH_TO_REACT_NATIVE_PROJECT/node_modules/react-native/react.gradle") } catch (ignored: Exception) { setupReactNativeRepository() }
Висновки
Як ви вже помітили, інтеграція React Native коду в наявний Android-застосунок — це складна задача, яка потребує багато зусиль та часу на реалізацію не лише на початкових етапах, а й при подальшій розробці. Робити таку інтеграцію чи ні — вирішувати вам. Такий підхід може бути релевантним, якщо нативні проєкти досить великі та повний перехід займає досить багато часу. В такому випадку нова функціональність може писатись на React Native, при тому паралельно в окремих модулях можна переписувати стару функціональність. Такий метод переходу на нову технологію є надійнішим в плані якості розробки.
Зі своєї сторони я опишу переваги та недоліки такого підходу.
Переваги
- Встановлення виробничих процесів та налаштування зв’язки React Native <-> Native — це лише одноразова задача, яка робиться на початку інтеграції
- Це може пришвидшити розробку як мінімум частини функціональності застосунків
- Компоненти застосунку, які виглядатимуть однаково
- Поведінка застосунку буде однаковою на всіх платформах
- Instant feature reload and hot reload options замість компіляції значно пришвидшує розробку
Ресурси:
Недоліки
- Потрібно декомпозувати логіку між нативною частиною та React Native. Особливої уваги заслуговують такі частини, як deeplinks, navigation, push notifications, тощо
- Розмір кінцевого .aab/.apk файлу швидше за все збільшиться, що може негативно вплинути на acquisition ваших користувачів
- Час компіляції виросте, навіть якщо ви виділите React Native flow в окремий модуль
- Складнощі при онбордингу на проєкт, необхідно виділяти багато часу на написання технічної документації
- Зміни виробничих процесів, що впливають на velocity команди
- Стає складніше керувати релізами
Ресурси:
Чи призвело це до підвищення ефективності на нашому проєкті? Неможливо сказати точно, оскільки velocity calculation збився через короткий таймлайн та процесуальні зміни в команді. Проте тепер ми майже не витрачаємо час на підтримку та функціональні зміни в signup flow, крім змін у навігації. За це тепер відповідає лише React Native команда.
Чи рекомендуємо ми інтегрувати React Native для частини застосунку? Ні, краще розпочинати розробку паралельно та намагатися наздогнати нативну розробку. Це короткострокові інвестиції, які потенційно (залежить від кейсу) можуть окупитись у майбутньому за рахунок зменшення команди нативних розробників або ж їх переводу в режим підтримки нативної функціональності.
29 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів