Sample report for a fictional app, generated with npx nativekeel plan --html report.html. Back to NativeKeel
acme-app health report
Findings
- CriticalReact Native 0.77.1 is no longer supported
Latest is 0.87.1. You are 10 minor versions behind; only the latest 3 minors receive fixes.
- CriticalNew Architecture is disabled (android + ios)
The legacy architecture was removed in 0.82. This app cannot upgrade past 0.81 until it migrates.
- CriticaltargetSdk 34 hides the app from users on newer Android versions
Since 2026-08-31 Google Play requires API 35+ for published apps to stay visible, and API 36+ to publish any update.
- CriticalAWS access key ID shipped inside the app (src/App.js:6)
Value AKIA…0E. Anyone who downloads the app can extract it. Revoke it now, then move the call to a server.
- Highreact-native-fast-image: no New Architecture support
Likely upgrade blocker (it may still run through the interop layer). Plan a replacement or a maintained fork. Alternatives: expo-image.
- Highreact-native-fs: unmaintained
No fixes will come for future React Native, iOS or Android releases. Alternatives: @dr.pogodin/react-native-fs, expo-file-system.
- High@react-native-community/push-notification-ios: no New Architecture support, unmaintained
Likely upgrade blocker (it may still run through the interop layer). Plan a replacement or a maintained fork. Alternatives: @react-native-firebase/messaging, expo-notifications.
- HighiOS AppDelegate is missing RCTAppDependencyProvider (ios/Acme/AppDelegate.mm)
Required since React Native 0.77. Without it, library Fabric components fall back to the legacy interop layer and send RCTEventEmitter events, which are not registered with the New Architecture: "RCTEventEmitter.receiveEvent() ... has not been registered as callable", logged in Debug, a launch crash in Release. It will surface as soon as the New Architecture is turned on. Add the import and
self.dependencyProvider = [RCTAppDependencyProvider new];before calling super. - HighRelease signing password committed (android/gradle.properties)
MYAPP_RELEASE_STORE_PASSWORD. Anyone with repository access has them. Read them from ~/.gradle/gradle.properties or environment variables instead, and change them if the keystore was ever shared.
- MediumFlipper is still integrated
Flipper was removed from React Native in 0.74 and its native libraries are not 16 KB aligned. Remove the Flipper dependencies and initialization code before upgrading; use React Native DevTools instead.
- MediumPodfile loads @react-native-community/cli-platform-ios/native_modules
React Native provides use_native_modules! itself. The CLI used from React Native 0.80 no longer ships this file, so pod install fails with "cannot load such file". Delete the require_relative line.
- MediumXcode project lacks -DFOLLY_CFG_NO_COROUTINES (ios/Acme.xcodeproj/project.pbxproj)
OTHER_CPLUSPLUSFLAGS comes from an older template. From React Native 0.80 an Objective-C++ AppDelegate fails to compile with "'folly/coro/Coroutine.h' file not found". Add "-DFOLLY_CFG_NO_COROUTINES=1" and "-DFOLLY_HAVE_CLOCK_GETTIME=1" next to "-DFOLLY_USE_LIBCPP=1" in both build configurations, as in the current template.
- MediumCleartext HTTP is allowed in the release manifest
android:usesCleartextTraffic="true" lets the app send data unencrypted. Keep it only in src/debug/AndroidManifest.xml (Metro needs it in development), or allow specific domains with a network security config.
- LowMainApplication.java calls getDefaultReactHost with two arguments (android/app/src/main/java/com/acme/MainApplication.java)
From React Native 0.80 this fails to compile from Java ("no suitable method found for getDefaultReactHost(Context,ReactNativeHost)"). Pass null as the third JSRuntimeFactory argument, or convert MainApplication to Kotlin like the template.
- LowiOS privacy manifest is generated at pod install, not committed
React Native 0.75+ creates PrivacyInfo.xcprivacy during
pod installwith the required-reason APIs of React Native and your pods. Commit it, then add what only you know: collected data types and tracking. App Store Connect checks those too. - Info1 patch-package patch: react-native-svg
Each was written against one library version and one React Native version. After every upgrade step check that each still applies and is still needed; a patch that adapted a library to an older React Native can break it on a newer one.
- InfominSdk 23 is below what current React Native supports (24)
Upgrading raises the minimum to Android API 24. Check how many of your users are on older Android versions first.
- InfoiOS deployment target 13.4 is below what current React Native supports (15.1)
Upgrading raises the minimum to iOS 15.1. Check how many of your users are on older iOS versions first.
4 packages are a major version behind (low)
| Package | Installed | Latest | |
|---|---|---|---|
| react-native-reanimated | 3.17.5 | 4.7.1 | native |
| react-native-gesture-handler | 2.25.0 | 3.3.0 | native |
| @react-navigation/drawer | 6.6.2 | 7.14.3 | |
| @react-native-community/slider | 4.5.7 | 5.2.1 | native |
Upgrade plan scope: Extra large
1. Stop the leaks
Leaked keys cost money and data from the moment someone finds them. Nothing else in this plan is as urgent.
- **Revoke the AWS access key ID** in
src/App.js:6at the provider today. Removing it from code is not enough: every installed copy of the app still contains it. - Move every call that needs a secret behind your own backend (a Cloud Function, API route or edge function). The app should only hold keys that are designed to be public.
- **Move the signing passwords out of
android/gradle.properties** into~/.gradle/gradle.propertiesor CI environment variables; change them if the keystore ever left your control. - The old values stay in git history. Rotation is what makes them useless; rewriting history (e.g.
git filter-repo) is optional cleanup.
2. Set up a safety net
A build that compiles and launches can still crash after login or hide a button. On a real upgrade, UI flows caught three such bugs that build and launch checks missed.
- Before changing anything, write a few UI flows (Maestro is the quickest: maestro.mobile.dev): launch, sign-in with a test account, and every tab or main screen.
- Keep test credentials in environment variables, never in the repo. Never let a flow press a button that writes to production (sign-up, purchase, posting).
- Assert that no error banner or red screen appears, not only that the app is running: a native module that fails to load can leave the process alive behind a red screen.
- Run the flows on both platforms on the current version, then again at the end of every phase below.
3. Meet the Google Play target SDK
Below the required level Google Play rejects updates or hides the app from new users.
- Set
targetSdkVersion = 36andcompileSdkVersion = 36inandroid/build.gradle. - Read the Android behavior changes for every API level between 34 and 36; edge-to-edge display and foreground service types are the usual surprises.
- Build a release bundle and test on a device running the newest Android version.
4. Meet App Store and Google Play requirements
These block your next store submission regardless of anything else in this plan.
- Run
pod install, commit the generatedPrivacyInfo.xcprivacy, and add your collected data types and tracking to it (React Native only fills in the required-reason APIs).
5. Tighten transport security
Unencrypted traffic exposes user data on public networks.
- Move
android:usesCleartextTraffic="true"toandroid/app/src/debug/AndroidManifest.xml, or allow only specific hosts with a network security config.
6. Remove legacy tooling
Leftovers from older templates break on newer React Native versions; removing them first shrinks the upgrade diff.
- When moving to React Native 0.80+, change
DefaultReactHost.getDefaultReactHost(context, host)inandroid/app/src/main/java/com/acme/MainApplication.javato passnullas a third argument. - In
ios/Acme.xcodeproj/project.pbxproj, add-DFOLLY_CFG_NO_COROUTINES=1and-DFOLLY_HAVE_CLOCK_GETTIME=1to OTHER_CPLUSPLUSFLAGS (Debug and Release) before moving to React Native 0.80. - Delete
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'fromios/Podfile;use_native_modules!comes from React Native. - Remove Flipper: delete the
com.facebook.flipperdependencies andFLIPPER_VERSIONon Android,use_flipper!/ Flipper config in the Podfile, and theReactNativeFlipperinitialization code. Use React Native DevTools instead.
7. Replace blocking and abandoned packages
These packages decide how far you can upgrade. Swap them while the app still runs on its current version, so every problem has one cause.
react-native-fast-image(no New Architecture support, native). Options:expo-image.npm uninstall react-native-fast-image && npm install expo-image(Expo packages need Expo modules first:npx install-expo-modules@latest)@react-native-community/push-notification-ios(no New Architecture support, unmaintained, native). Options:@react-native-firebase/messaging,expo-notifications.npm uninstall @react-native-community/push-notification-ios && npm install @react-native-firebase/messagingreact-native-fs(unmaintained, native). Options:@dr.pogodin/react-native-fs,expo-file-system.npm uninstall react-native-fs && npm install @dr.pogodin/react-native-fs
8. Update native modules on the current React Native version
Newer releases of native modules usually add New Architecture support. Pick the newest release that still supports your current React Native version; check each changelog for its minimum.
react-native-reanimated3.17.5 → 4.7.1react-native-gesture-handler2.25.0 → 3.3.0@react-native-community/slider4.5.7 → 5.2.1
9. Turn on the New Architecture on your current version
React Native 0.82 removed the legacy architecture. Switching now, before changing the React Native version, keeps the two kinds of breakage apart.
- In
ios/Acme/AppDelegate.mm, add#import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>andself.dependencyProvider = [RCTAppDependencyProvider new];before calling super. Without it the switch causes a launch crash in Release. - Android: set
newArchEnabled=trueinandroid/gradle.properties. - iOS: set
"newArchEnabled": "true"inios/Podfile.properties.json(or runRCT_NEW_ARCH_ENABLED=1 bundle exec pod install). - Clean everything (
./gradlew clean,watchman watch-del-all, deleteios/buildandios/Pods). Switching architecture without a clean build fails on stale codegen output (e.g. missingNative…Specclasses). - Build debug and release on both platforms.
- Click through every screen that uses a native module. Interop-layer problems show up at runtime, not at build time.
10. Raise platform minimums
Current React Native no longer supports older OS versions. Check your analytics for how many users this drops before you ship.
- Android minSdk → 24
- iOS deployment target → 15.1
11. Upgrade React Native 0.77.1 → 0.87.1
Hopping a few minors at a time gives small diffs you can actually review and bisect when something breaks.
- 0.77.1 → 0.80.0: apply the diff from https://react-native-community.github.io/upgrade-helper/?from=0.77.1&to=0.80.0, move
react-native-screens4.14–4.18.x andreact-native-reanimated3.19.x (or 4.2.x, a major migration) andreact-native-gesture-handler2.28–2.31.x, reinstall pods, build both platforms, commit. - 0.80.0 → 0.83.0: apply the diff from https://react-native-community.github.io/upgrade-helper/?from=0.80.0&to=0.83.0, move
react-native-screens4.25.x andreact-native-reanimated4.6.x (a major migration: read its migration guide first), reinstall pods, build both platforms, commit. - 0.83.0 → 0.86.0: apply the diff from https://react-native-community.github.io/upgrade-helper/?from=0.83.0&to=0.86.0, move
react-native-screens4.26+ andreact-native-reanimated4.8.x andreact-native-gesture-handler2.32–2.99.x (or 3.0+, a major migration), reinstall pods, build both platforms, commit. - 0.86.0 → 0.87.1: apply the diff from https://react-native-community.github.io/upgrade-helper/?from=0.86.0&to=0.87.1, reinstall pods, build both platforms, commit.
- Bump
react,@react-native/*packages and the Metro/Babel config to the versions the Upgrade Helper shows for the target. - After every hop, re-check
patches/react-native-svg+15.15.5.patch: still applies (npx patch-package), still needed, not undoing what the new version expects. - If
pod installreports that a React Native dependency (fast_float, hermes-engine, …) differs from Podfile.lock, deleteios/Podsandios/Podfile.lockand install again; library pods keep the versions their podspecs pin. - Update the Gradle version by editing
android/gradle/wrapper/gradle-wrapper.properties:./gradlew wrapperfails because the new React Native Gradle plugin already needs the new Gradle.
12. Update JavaScript-only packages
These do not block the upgrade. Do them last, one major at a time, following each migration guide.
@react-navigation/drawer6.6.2 → 7.14.3
13. Verify before you ship
An upgrade is done when the release build works on real devices, not when it compiles.
- Release builds on both platforms (
./gradlew bundleRelease, Xcode Archive). - Test on a low-end Android device and on the newest iOS and Android versions.
- Ship to internal testing / TestFlight first and watch crash-free sessions for a few days before a staged rollout.
- Run
npx nativekeelagain: the report should have no critical or high findings.