Deploying React-Native Apps
This guide was created on November 1, 2019. If this datum is a few months in the past, you will probably have to update this guide.
Setup your machine
In this section, I describe the machine setup I used at time of writing. Up-to-date documentation to get started is available on the React Native website.
The following sections cover standard React-Native. For Expo, jump to "Expo" below.
JavaScript Dependencies
First install the JavaScript dependencies:
brew install yarn
brew install node
brew install watchman
yarn global remove react-native-cli # If necessary
yarn global add @react-native-community/cli
iOS Dependencies
Now Install XCode via App Store.
Afterwards, install the Xcode Command Line Tools. Open Xcode, then choose "Preferences..." from the Xcode menu. Go to the Locations panel and install the tools by selecting the most recent version in the Command Line Tools dropdown.
Finally install cocoapods.
sudo gem install cocoapods # Note: also upgrades...
If you're using a M1 chip, the above command might not work for you. See https://github.com/CocoaPods/CocoaPods/issues/9907. This can be solved in a few different ways. The easiest solution is:
- Undo step above
sudo gem uninstall cocoapods
- Install cocoapods via homebrew
brew install cocoapods
Android Dependencies
Install JDK:
brew tap AdoptOpenJDK/openjdk
brew cask install adoptopenjdk8
Next, install Android Studio with a custom setup. Select: Android SDK, Android SDK Platform, Performance (Intel ® HAXM), Android Virtual Device.
After installation, open the SDK Manager in Android Studio, check "Show Package Details" and install the Android 9 (Pie) SDK with the following options: Android SDK Platform 28 and Google APIs Intel x86 Atom System Image.
Next, select the "SDK Tools" tab and check the box next to "Show Package Details" here as well. Look for and expand the "Android SDK Build-Tools" entry, then make sure that 28.0.3 is selected.
Now, to create a virtual device, open the AVD Manager, select "Create Virtual Device...", then pick any Phone from the list and click "Next", then select the Pie API Level 28 image.
Expo
Covered online.
Creating an app
Information collection
To set up a new app, we need some stuff from the customer. To avoid pingponging, here is a complete list of things we might need from them:
iOS
- Access to an Apple Developer. The customer has to enroll for the Apple Developer Program and give us access (it is best if we immediately pass the Apple IDs of everyone who needs access to the customer)
- Company name. The name of the company as it should appear in the App Store.
- App name (max. 30 characters). The app name as it should appear in the app store
- Subtitle (max. 30 characters). Summary of the app that appears below the app name.
- Privacy Policy URL. A privacy policy is required for all apps in the app store.
- Category (required) and Secondary Category (optional): one of Books, Business, Education, Entertainment, Finance, Food & Drink, Games, Health & Fitness, Lifestyle, Magazine & Newspapers, Medical, Music, Navigation, News, Photo & Video, Productivity, Reference, Shopping, Social Networking, Sports, Stickers, Travel, Utilities, Weather.
- Description (max. 4000 characters): a description of your app that highlights its features and functionality.
- TestFlight Feedback email: TestFlight Beta Testers can send feedback to this email address. and the email address will also appear as the reply-to address for TestFlight invitation emails.
- Beta App Review Information: information of the person to be contacted for App Review (Apple reviews the app when
external testers are invited for TestFlight; if only App Store Connect users are invited, an App Review is not necessary).
- Contact person: first name, last name, phone number and email address. If the customer is ok with it, we can enter information of someone from Codifly.
- Sign-In Information: username and password that Apple staff can use to get access to all functionality in the app.
Android
Not documented yet
iOS
Getting access
First, we have to create an app for iOS. Typically, the customer enrolls for the Apple Developer Program, which gives them access to TestFlight, the App Store, App Analytics ...
For us to be able to do the necessary configuration, the customer has to create a user in App Store Connect. He can create a user on Users and Access. For us to have sufficient privileges, the created user has to have the Admin role.
As soon as we have an account, we can log in on both Apple Developer and App Store Connect (in the past, both online platforms were rather independent from each other, but recently they have been merged and somewhat integrated). In both tools, always select the correct team in the upper-right corner. This is important: everything you see and can do is in the context of the team you have selected, and you don't want to create the Collect&Go app in the Gingerwald team.
Register the App ID
First, we will create the app. Go to Apple Developer, make sure the correct team is selected in the upper-right corner and click "Certificates, IDs & Profiles". Next, click "Identifiers" and click the Plus icon. At "Register a New Identifier", select "App IDs" and click "Continue".
In the next screen, enter the following values and click "Continue":
- For "Platform", select "iOS, tvOS, watchOS".
- For "Description", be creative.
- For "Bundle ID": select "Explicit" and enter the bundle identifier configured in the source code. For standard React
Native apps, the app identifier can be found/configured in
iOS/<appName>/Info.plistkeyCFBundleIdentifier(it might be a reference to a variable in the .pbxproj). For Expo apps, the bundle identifier can be found inapp.jsonatios.bundleIdentifier(add if not already present). - For "Capabilities": make sure that those match the entitlements of the XCode-project.
As a last step, review all entered information and click "Register".
Set up App Store Connect App
Go to App Store Connect, and go to "My Apps". Click the blue "Plus"-sign at the top-left and select "New App".
Enter the following information and click Create:
- "Platforms": check "iOS" and uncheck "tvOS"
- "Company name": enter the company name that will be visible in the App Store (can not be changed after creation!)
- "Name" (max. 30 characters): the app name as will be visible in the App Store.
- "Primary Language": self-explanatory, probable Dutch or some English variant.
- "Bundle ID": select the bundle identifier created in the previous section.
- "SKU": a unique identifier for the app. As a quick solution, use the bundle identifier.
- "User Access": select "Full Access".
Configure the local project
Standard React Native
TODO: More docs are necessary here: app icon, ...
Expo
First, make sure that app.json is correctly configured:
- Key
ios.bundleIdentifier: value should match the App ID created above. - Key
name: value should match the App name entered earlier. - key
icon: should point to a png-file that does not contain transparency (!). With image magick, transparency can easily be removed:convert icon.png -background black -alpha remove -alpha off icon.png
Android
TODO: Not documented yet.
Test builds
Prepping for a (new) release
First, make sure everything is merged in the correct branch, and that you are on the correct commit and that you have no uncommitted changes. Then follow the following instructions.
Standard React Native
Make sure your dependencies are up-to-date.
yarn
(cd ios && pod install --repo-update)
Next bump your version:
export NEW_VERSION=1.2.3 # Increment once for each new "real" version
export NEW_ANDROID_VERSIONCODE=1002003 # Match NEW_VERSION
export NEW_IOS_BUILD_NUMBER=1 # Increment for each "retry", being an upload to App Store Connect)
# Update version in package.json manually, set to NEW_VERSION
# iOS
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString '${NEW_VERSION}'" ios/<Project>/Info.plist
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion '${NEW_IOS_BUILD_NUMBER}'" ios/<Project>/Info.plist
# iOS, In case of One Signal Push notifications
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString '${NEW_VERSION}'" ios/OneSignalNotificationServiceExtension/Info.plist
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion '${NEW_IOS_BUILD_NUMBER}'" ios/OneSignalNotificationServiceExtension/Info.plist
# Android
sed -i "" "s/versionCode [0-9]*/versionCode ${NEW_ANDROID_VERSIONCODE}/" android/app/build.gradle
sed -i "" "s/versionName \".*\"/versionName \"${NEW_VERSION}\"/" android/app/build.gradle
Commit your changes in a commit with message: "chore: version bump to x.y.z".
Expo
Make sure your dependencies are up-to-date.
yarn
Update the version number in your package.json (if present) and in you app.json. Commit your changes in a commit
with message: "chore: version bump to x.y.z".
iOS
Background Information
On iOS the process is very complex, and it involves provisioning profiles, signing certificates, .... However, if you have sufficient privileges on Apple Developer, there shouldn't be too many problems as XCode can do almost everything automatically. TODO: In the future, I might write out the whole process.
One time setup of TestFlight
Configuration of the app for internal testing starts in tab "App Store" section "App Information":
- "Subtitle" (max. 30 characters). Summary of the app that appears below the app name.
- "Privacy Policy URL". A privacy policy is required for all apps in the app store.
- "Category" (required) and Secondary Category (optional): one of Books, Business, Education, Entertainment, Finance, Food & Drink, Games, Health & Fitness, Lifestyle, Magazine & Newspapers, Medical, Music, Navigation, News, Photo & Video, Productivity, Reference, Shopping, Social Networking, Sports, Stickers, Travel, Utilities, Weather.
We continue in tab "TestFlight" section "Test Information":
- "Beta App Description" (max. 4000 characters): a description of your app that highlights its features and functionality.
- "TestFlight Feedback email": TestFlight Beta Testers can send feedback to this email address. and the email address will also appear as the reply-to address for TestFlight invitation emails.
- "Beta App Review Information": information of the person to be contacted for App Review (Apple reviews the app when
external testers are invited for TestFlight; if only App Store Connect users are invited, an App Review is not necessary).
- "Contact person": first name, last name, phone number and email address
- "Sign-In Information": username and password that Apple staff can use to get access to all functionality in the app.
Building: Getting an IPA
The following steps are for standard React-Native. For Expo, jump to the bottom of this section.
The first time
TODO: more steps are necessary here, select the team in XCode etc.)
Create a file ios/ExportOptions.plist with the following contents:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>teamID</key>
<string>YOUR_TEAM_ID_AS_VISIBLE_AT_DEVELOPER.APPLE.COM</string>
<key>destination</key>
<string>upload</string>
</dict>
</plist>
Creating builds
Sometimes it is a good idea to run the packager (IN A SEPARATE TAB). Note: react-native start bugs at the moment
("Error: Unable to resolve module ./../react-transform-hmr/lib/index"), butyarn start` works.
yarn start
Create an Archive. Build for “Generic iOS device” using the "Archive" menu item in XCode itself (don't forget to open the workspace, not the project). After archiving has completed, the "Organizer" window will appear. Select your newly created archive and click "Distribute App". Follow the steps to upload to TestFlight.
- Select "App Store Connect" and click "Next".
- Select "Upload" and click "Next".
- Wait.
- Deselect "Include bitcode" and "Upload symbols". Click Next
- Select "Automatically manage signing" and click Next.
- Click "Upload" and wait.
Here below is a script that should to the same thing, but this is untested.
cd ios
xcodebuild archive -scheme <App> -workspace <App>.xcworkspace -configuration Release -archivePath build
xcodebuild -allowProvisioningUpdates -exportArchive -archivePath build.xcarchive -exportPath app.ipa -exportOptionsPlist ExportOptions.plist
# The IPA should have been automatically uploaded to TestFlight.
Alternative: building in Expo
We follow this guide. Run
expo build:ios. An interactive wizard will start. It might be necessary to launch the command multiple times,
depending on the scenarios listed below.
First, if a message appears that expo cli is outdated, abort and update it. We don't want deprecation shit. The re-execute the command.
If you have not authenticated already yourself to Expo, you will first have to create an Expo account. You will have to enter your e-mail address, the username and a password. Don't forget to store your information in 1Password.
If everything went succesful, enter your "Apple ID" (mail address of the Apple account that has access to the Developer Program) and "Password". If you have 2FA enabled, you will now have to enter your 6 digit 2FA-code. (note: the first time, the wizard crashed with error "Authentication with Apple Developer Portal failed!", however, the second time it worked). Next, select your team from the list.
Next, it is time for the real Apple fun: the Distribution Certificate, the Push Notifications service key and the Provisioning Profile. The easiest option is to select "Expo handles all credentials, you can still provide overrides" and "Let Expo handle the process" (twice). All magic will be done for us (if we have sufficient priveleges with our Apple ID).
Note: in my case it now crashed with message "connect ECONNREFUSED 127.0.0.1:19001". I had to run expo start in a
separate tab and try again to make it work.
When everything is done, a message "Successfully built standalone app: ..." will appear. Click the link to download the resulting IPA file. To upload the IPA file, open the Transporter app (which can be installed on your Mac by downloading it from the App Store). Drag the IPA file on Transporter, and click "Lever af".
Deployment
Make builds available for App Store Connect Users
After completion of the upload, go to App Store Connect, tab Testflight, menu item Builds. The new build will appear in the list. Note that it might briefly disappear again but after a few minutes it should come back in the Processing state! When processing is done, you will receive a mail "App Store Connect: ... for ... has completed processing.". This might take some time (typically 5 minutes, but the first time it might take 30+ minutes).
In the meantime while you are waiting, you will probably get a mail with some bad news. The complain mail will have subject "App Store Connect: Your app "..." (...) has one or more issues". Fortunately, most issues are often easily fixed. However, you might have to upload a few IPA's before Apple's fully happy.
Anyways, after processing has completed, go again to App Store Connect, tab Testflight, menu item Builds. The new build will now have status "Missing Compliance". Click the build number (the blue number), and click "Provide Export Compliance Information". Click "No" and click "Start Internal Testing" (yes, you will have to do this every time).
The status of your app will now be "Ready to test" for "App Store Connect Users". Ta-daaam. All App Store Connect users will now be able to download the app using Testflight!
If this is your first upload, don't forget to go to App Store Connect Users and add yourself as tester.
A note on inviting new App Store Connect Users to test your app
To invite new people to test the app, ask their OS (if they have an Android, "pech" for them) and Apple ID.
Open App Store Connect and go to "Users and Groups" and check if already were added. If not,invite them to App Store Connect (you can give him role Marketing and only give him access to your app instead of All Apps). Only after they have clicked the link in the invitation mail, you can invite them for TestFlight. Ask them to send you a confirmation via mail: you won't get one from Apple.
Next, go to "App Store Connect", "My Apps", click your app, open the "TestFlight" tab, click the "App Store Connect Users" section and search for the Apple ID to add the new user as a tester. The user will receive a mail with an invitation for TestFlight. In this mail will be a big blue button with a TestFlight Redeem code. The user will have to install TestFlight, which can be downloaded in the App Store. Afterwards, the user can open TestFlight on his iOS device, click "Redeem" (Dutch: "Wissel in") and enter the code. The app can now easily be installed and the user will get a notification everytime a new version is uploaded to TestFlight.
Android
Background Information
The Keystore
We all know some basics about asymmetric encryption. We can sign something with a private key and other can then verify the signature with the corresponding public key, which is called a certificate.
In Android, all apps should be signed with a private key. The private key and its public certificate are stored in a
*.keystore-file. This certificate is called the app signing certificate. The big problem is that losing the
private key makes it impossible to upload new versions to Google Play.
But, and this is very important, Google now has App Signing by Google Play. When this feature is enabled, apps are not signed with an app signing certificate but with an upload certificate. After uploading the app to Google Play, the upload certificate is replaced by an app signing certificate managed by Google Play.
From the signing process's perspective, there is no difference whether an app is signed with an upload certificate or an app signing certificate. The only difference is: you have to tell Google what your upload certificate is and Google Play will do everything for you.
In what follows, we have written down the steps for using upload certificates, which is now the recommended approach.
APK vs AAB (App Bundle)
When building, we can create an APK or an AAB-file (also called a bundle). APK-files are easy to install on any Android device, but AAB-files are the recommended approach to upload builds to the Store. However, note that App Signing by Google Play should be enabled before AAB-files are supported.
In short:
- Use APK-files to install on individual devices.
- Use AAB-files to deploy via Google Play (including internal, alpha & beta builds) to many devices.
Building: getting an APK/AAB
The following steps cover Standard React native. For Expo, jump to "Alternative: Building in Expo" below.
First time: set up keystore
If this is the first time, we will have to create a keystore-file. Run the following command. In some interactive wizard
you will have to provide some information, including a secret with which the keystore file will be encrypted. Move the
newly generated xxx-upload-key.keystore file under the android/app directory in the project folder.
sudo keytool -genkey -v -keystore <app_name>-upload-key.keystore -alias <app_name>-key-alias -keyalg RSA -keysize 2048 -validity 10000
Next, edit the file ~/.gradle/gradle.properties, and add:
<APP_NAME>_STORE_FILE=<app_name>-upload-key.keystore
<APP_NAME>_STORE_PASSWORD=xxx
<APP_NAME>_KEY_ALIAS=<app_name>-key-alias
<APP_NAME>_KEY_PASSWORD=xxx
Finally, add the following to android/app/build.gradle and commit your changes:
...
android {
...
signingConfigs {
...
release {
storeFile file(<APP_NAME>_STORE_FILE)
storePassword <APP_NAME>_STORE_PASSWORD
keyAlias <APP_NAME>_KEY_ALIAS
keyPassword <APP_NAME>_KEY_PASSWORD
}
}
buildTypes {
release {
...
signingConfig signingConfigs.release
}
}
...
}
...
Note that the generated keystore file is in gitignore. As a result, add the keystore-file to 1Password. Also, the edited gradle.properties-file is in your home directory. Thus, also add the newly added lines to 1Password. This is important: otherwise no other developers will be able to upload new builds!
Make a build
Sometimes it is a good idea to run the packager (IN A SEPARATE TAB). Note: react-native start bugs at the moment
("Error: Unable to resolve module ./../react-transform-hmr/lib/index"), butyarn start` works.
yarn start
Now create an APK and AAB file:
(cd android && ./gradlew assembleRelease && ./gradlew bundleRelease)
cp android/app/build/outputs/apk/release/app-release.apk ~/Desktop/app.apk
cp android/app/build/outputs/bundle/release/app.aab ~/Desktop/app.aab
Alternative: Building in Expo
We follow this guide. Run
expo build:android -t app-bundle (AAB) or expo build:android -t apk (APK). An interactive wizard will start.
It might be necessary to launch the command multiple times, depending on the scenarios listed below.
First, if a message appears that expo cli is outdated, abort and update it. We don't want deprecation shit. The re-execute the command.
If you have not authenticated already yourself to Expo, you will first have to create an Expo account. You will have to enter your e-mail address, the username and a password. Don't forget to store your information in 1Password.
If this is the first time, expo will ask us whether we want to use an existing keystore or generate a new one. Select "Let Expo handle the process". This will create a fancy upload certificate. If this is the first test build, there are two additional steps:
- We have to upload the upload certificate to Google Play. First run
expo fetch:android:upload-certto extract the public certificate from the keystore and save it in a PEM-file. Next, open the Google Play console, and go to "Releasebeheer" > "App-ondertekening" to upload your PEM-file. - We have to download the keystore file from the Expo-server and store it in our 1Password, so that other developers can
make releases too! Run
expo fetch:android:keystoreand add the resulting file on 1Password. Also add the keystore password, key alias, and key password to 1Password.
When everything is done, a message "Successfully built standalone app: ..." will appear. Click the link to download the resulting APK/AAB file.
Deployment
Delivering to individuals
You can easily verify an APK-build in an emulator :
# Test in emulator
emulator @$(emulator -list-avds | head -n 1) & # Launch an emulator
react-native run-android --variant=release # Run your newly created release
To install the APK on a device, simply transfer it to that device (WeTransfer, Google Drive, or any other method).
Delivering to many devices
If the build is verified, we can grab the AAB-file and upload it to the Google Play Console.
TODO: write out in more detail; discuss internal/beta/alpha/...
Beta Testing
iOS
Make the App available for External Testers
In case you also want external testers to have access, click "Add External Testers". At time of writing, I have no experience in this. However, your app should have status "Ready to Submit" for "External Testers". You will have to submit your app to Apple, they will test it, and then your external testers will gain access. I suppose the test by Apply staff will take 1+ days, so your external testers will have to have some patience.
Android
Not documented yet
Move to production
Prerequisites
To get ready for production, we need some stuff from the customer. To avoid pingponging, here is a complete list of things we might need from them:
- Price: the app can be free or [0 - 999].99 dollars. The currency is converted by Apple to the local currency of the user.
- Availability: the list of countries in which the app can be downloaded. Either all countries or a specific set of countries.
- Access to Codifly's Expo account invited by the the client to his Expo account
- Access to the clients Apple Developer team via Codifly's no-reply account. Invited as "Developer", but higher roles are nice to have.
- Access to the clients Google Play Console organisation. Invited as "Developer", but higher roles are nice to have.
TODO: Not fully documented yet. We need a lot more
One time setup
We continue in tab "App Store" section "Pricing and Availability":
- Price: the app can be free or [0 - 999].99 dollars. The currency is converted by Apple to the local currency of the user.
- Availability: the list of countries in which the app can be downloaded. Either all countries or a specific set of countries.
Finally, we go to the tab "App Store" and click the item at the left under "IOS APP", probably named "1.0 Prepare for submission".
TODO: Not fully documented yet.
Structure

For each Expo project we want to have the following structure:
- 1 Expo Project
- This is setup in the clients organisation
- An app in App Store Connect per published environment:
- Staging
- Production
- ...
- An app in Google Play Console per published environment:
- Staging
- Production
- ...
We want to take this approach for the following reasons:
- Staging builds are distributed via a real store infrastructure. This is good and easy for testing.
- No risk of accidentally publishing the staging environment into production.
- You can have both the staging and production app installed simultaneously.
- Separate credentials, keys and analytics if needed.
- Only 1 App Store Connect and Google Play Developer account needed.
The only disadvantage is:
- More setup
We're willing to take this risk :)
Credentials
We use Expo managed credentials. This makes it easier to manage as credentials will be stored in EAS servers and everyone can simply deploy with the right account. No hassle of copy pasting .keystore files, Apple distribution certificates, etc.
Android
For Android / Google Play Console, we use Google Managed Signing in combination with Expo managed credentials.
This means that we still need to create a .keystore file (once). This will be created using EAS and stored in EAS Credentials. Expo keeps the credentials in their server. BUT with all this managed signing, losing the .keystore file is no longer a catastrophic event. The .keystore file we create simply becomes the "Upload key". It's used to very that we are the developer of app X. When the .aab is uploaded with this keystore to the Google Play Console, Google verifies this and then resigns it with their managed signing credentials. These are stored in Google servers. We can no longer lose the master key. Hooray!
In case we were to lose the "Upload Key", we can request a reset with Google support.
iOS
iOS has a list of different items:
- Certificates (Who)
- Development
- Build
- App IDs (What)
- Entitlements and capabilities (Permissions)
- Provisioning profiles (The glue)
If the Certificate is your Driver's License (Identity), the Provisioning Profile is the Rental Car Contract. It links You (Certificate) to a specific Car (App ID) with specific Insurance (Entitlements) and lists who is allowed to Drive (Device UUIDs).
With Expo managed credentials, managing these become quite easy. When building applications you can login with your Apple Developer account. Expo can then fetch information such as what certificates there are, app identifiers, which provisioning profiles you have, etc.
We have to setup the build certificate only once, afterwards it will be stored in EAS servers.
If you have a higher role than "Developer" in ASC, you should create the credentials using Expo EAS. You can do this with eas credentials or eas build which will then prompt for credentials.
If you don't have a higher role than "Developer", Expo EAS will have a hard time with Apple permissions. What might be best is to ask the admin to either create a certificate if not already done or export an existing one. Either way, the admin should export a .p12 of the certificate with a password. You can then double click this, enter the password et voila. You now have the distribution certificate. Expo can then fetch this using EAS credentials. If you're still stuck further down the line, you can also manually upload it using Expo Dashboard.
Creating and uploading production builds
The process is similar to creating a test build. Off course, make sure your build points to the correct environment (production, not acceptance, testing, development or something else).
I want TestFlight to only contain test builds, not the production builds (otherwise, our dear testers might fuck up production data). So what I do: I create a production build, upload it to TestFlight. As soon as it is processed, I quickly install it and I expire the build in TestFlight. I can now validate the build. When everything works correctly, I can go to the "App Store" tab and release the build to the store. Indeed, a build that is expired in TestFlight can still be selected to upload to the App Store.
Android
TODO: Not documented yet.
I'm fucked
If building fails and nothing works...
# Cleanup JS stuff
rm -rf node_modules
yarn cache clean
watchman watch-del-all
rm -rf $TMPDIR/react-native-packager-cache-*
rm -rf $TMPDIR/metro-bundler-cache-*
# Cleanup iOS stuff (when having problems with iOS build)
cd ios
pod cache clean --all
rm -rf Pods/
rm -rf ~/Library/Developer/Xcode/DerivedData
rm -rf build/
xcodebuild clean
cd ..
# Delete Android stuff (when having problems with Android build)
rm -rf ~/.gradle/caches/*
cd android
rm -rf app/build
(cd .. && yarn) # Necessary for next step
./gradlew cleanbuildcache
./gradlew clean
cd ..
Troubles with Expo
Eject your app. Sorry.