This is an Expo project created with create-expo-app.
-
Install dependencies
npm install
-
Set up environment variables
Create a
.envfile in the project root:EXPO_PUBLIC_WEB3FORMS_KEY=your_key_here EXPO_PUBLIC_FEEDBACK_EMAIL=your_email@example.com EXPO_PUBLIC_SENTRY_DSN=your_sentry_dsn_here
EXPO_PUBLIC_WEB3FORMS_KEY- API key for the feedback form. Get a free key at https://web3forms.comEXPO_PUBLIC_FEEDBACK_EMAIL- Email address to receive user feedbackEXPO_PUBLIC_SENTRY_DSN- Sentry DSN for error logging. Get one at https://sentry.io
-
Start the app
npx expo start
In the output, you'll find options to open the app in a
- development build
- Android emulator
- iOS simulator
- Expo Go, a limited sandbox for trying out app development with Expo
You can start developing by editing the files inside the app directory. This project uses file-based routing.
First, generate the native projects:
npx expo prebuildnpx expo run:iosOr open in Xcode:
open ios/flipset.xcworkspacenpx expo run:androidBuild a release APK:
cd android && ./gradlew assembleReleaseThe APK will be at android/app/build/outputs/apk/release/app-release.apk. Install via:
adb install android/app/build/outputs/apk/release/app-release.apkOr upload the APK to Google Drive/Dropbox and download on your device.
keytool -genkeypair -v -storetype PKCS12 -keystore android/app/release.keystore -alias flipset -keyalg RSA -keysize 2048 -validity 10000Remember the password you set. Add to .gitignore:
echo "android/app/release.keystore" >> .gitignoreBack up the keystore outside the project (it gets deleted with prebuild --clean):
cp android/app/release.keystore ~/flipset-release.keystoreStore credentials in your user-level gradle.properties (not the project one) so they survive prebuild --clean:
echo "FLIPSET_UPLOAD_STORE_FILE=release.keystore" >> ~/.gradle/gradle.properties
echo "FLIPSET_UPLOAD_KEY_ALIAS=flipset" >> ~/.gradle/gradle.properties
echo "FLIPSET_UPLOAD_STORE_PASSWORD=your_password" >> ~/.gradle/gradle.properties
echo "FLIPSET_UPLOAD_KEY_PASSWORD=your_password" >> ~/.gradle/gradle.propertiesAdd release signing config:
signingConfigs {
debug { ... }
release {
if (project.hasProperty('FLIPSET_UPLOAD_STORE_FILE')) {
storeFile file(FLIPSET_UPLOAD_STORE_FILE)
storePassword FLIPSET_UPLOAD_STORE_PASSWORD
keyAlias FLIPSET_UPLOAD_KEY_ALIAS
keyPassword FLIPSET_UPLOAD_KEY_PASSWORD
}
}
}Update buildTypes.release:
release {
signingConfig signingConfigs.release
// ... rest of config
}cd android && ./gradlew bundleReleaseThe AAB will be at android/app/build/outputs/bundle/release/app-release.aab.
- Go to Google Play Console
- Select your app → Testing → Internal testing
- Create new release → Upload the AAB
- Add testers under the Testers tab
- Share the opt-in link with testers
Important: Back up your release.keystore file. Google Play App Signing manages the distribution key, but you need the upload key to publish updates.
Running prebuild --clean deletes the entire android/ folder. Your credentials are safe in ~/.gradle/gradle.properties, but you need to:
-
Copy your keystore back:
cp ~/path/to/backup/release.keystore android/app/ -
Re-add the signing config to
android/app/build.gradle: -
In
signingConfigs, add afterdebug:
release {
if (project.hasProperty('FLIPSET_UPLOAD_STORE_FILE')) {
storeFile file(FLIPSET_UPLOAD_STORE_FILE)
storePassword FLIPSET_UPLOAD_STORE_PASSWORD
keyAlias FLIPSET_UPLOAD_KEY_ALIAS
keyPassword FLIPSET_UPLOAD_KEY_PASSWORD
}
}- In
buildTypes.release, change:
signingConfig signingConfigs.debugto:
signingConfig signingConfigs.release-
Open the project in Xcode:
open ios/flipset.xcworkspace
-
Select your device as the build target
-
Change to Release configuration:
- Product → Scheme → Edit Scheme
- Select Run on the left
- Set Build Configuration to "Release"
-
Build and run (Cmd+R)
Note: With a free Apple Developer account, apps expire after 7 days. A paid account ($99/year) removes this limitation.
The app uses expo-image-picker + @bsky.app/expo-image-crop-tool + @react-native-ml-kit/text-recognition for OCR.
Other libraries considered:
react-native-image-crop-picker- Corrupts EXIF orientation on iOS, causing garbled OCR resultsexpo-image-pickerwithallowsEditing: true- Works but no drag handles for flexible croppingexpo-text-extractor- Vision Framework OCR, same iOS cropping issues as ML Kitexpo-image-manipulator- Tried to fix orientation post-crop, didn't resolve the issue
When you're ready, run:
npm run reset-projectThis command will move the starter code to the app-example directory and create a blank app directory where you can start developing.
To learn more about developing your project with Expo, look at the following resources:
- Expo documentation: Learn fundamentals, or go into advanced topics with our guides.
- Learn Expo tutorial: Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
Join our community of developers creating universal apps.
- Expo on GitHub: View our open source platform and contribute.
- Discord community: Chat with Expo users and ask questions.