Build and release
signed builds for the App Store, Google Play and the desktop.
Release builds use your own Apple team and Android signing key. Do the white-label steps first: bundle IDs and signing keys cannot change once an app is published.
01Before the first release build
- Your app names, colours, bundle IDs and icons are set (White label).
- Your card domain serves the app-link files with your Team ID (Card host setup).
- Your own RPC providers and API keys are in place (Networks, Configuration).
- The tests pass in each app (commands).
- Raise
versioninpubspec.yaml(andAPP_VERSION/APP_BUILDin NFC Crypto's.env) for every store upload.
02Android release
- Create an upload key once and keep it safe (losing it blocks updates):
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload - Create
android/key.propertiesin the app folder (git ignores it):WindowsThe commands on this page are written for macOS and Linux shells. On Windows run them in PowerShell, Git Bash or WSL; iOS builds need a Mac.
Use the full path forstoreFile=/path/to/upload-keystore.jks storePassword=your-store-password keyAlias=upload keyPassword=your-key-passwordstoreFile. - Build the bundle for Google Play.
| App | Signing | Build command |
|---|---|---|
| NFC Crypto | android/key.properties as above, or the variables ANDROID_KEYSTORE_PATH, ANDROID_KEYSTORE_PASSWORD, ANDROID_KEY_ALIAS, ANDROID_KEY_PASSWORD (for CI). Without them, the release build falls back to the debug key. | flutter build appbundle --release --dart-define-from-file=.env |
| CryptoCard Wallet | android/key.properties as above. Without it, the release build uses the debug key. | flutter build appbundle --release |
| NFC Recovery | Always the debug key: android/app/build.gradle.kts has no release signing config. Add one (as in the Flutter Android deployment guide) before publishing. | flutter build appbundle --release |
The bundle is in build/app/outputs/bundle/release/app-release.aab. For a direct install test use flutter build apk --release (build/app/outputs/flutter-apk/app-release.apk).
A debug key is the same on every developer machine. Check with keytool -printcert -jarfile app-release.apk that the certificate is yours.
After the first upload with Play App Signing, copy the "App signing key certificate" SHA-256 into assetlinks.json (details).
03iOS release
- Set your team (Set your Apple team) and make sure the App ID has NFC Tag Reading, plus Associated Domains for NFC Crypto and CryptoCard.
- Install pods if Flutter has not done it yet:
CocoaPods can fail on a non-UTF-8 locale; settingcd ios LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 pod install cd ..LANGavoids it. NFC Crypto'sPodfilesets it already; the other two apps do not. - Build the archive:
# NFC Crypto flutter build ipa --release --dart-define-from-file=.env # CryptoCard Wallet, NFC Recovery flutter build ipa --release - Upload
build/ios/ipa/*.ipawith Xcode's Organizer or Apple's Transporter app, or openbuild/ios/archive/Runner.xcarchivein Xcode and choose Distribute App.
NFC Crypto also has ios/ExportOptions.plist (App Store Connect) and ios/ExportOptions_AdHoc.plist (release testing). Put your Team ID in place of YOUR_TEAM_ID before you use them, for example with flutter build ipa --release --dart-define-from-file=.env --export-options-plist=ios/ExportOptions.plist.
04Simulator builds
flutter build ios --simulator --no-codesign
Simulators have no NFC; use them to check screens. With Xcode 27 on Apple silicon, the simulator app must be built for arm64 only. All three Flutter apps already set this in their ios/Flutter/*.xcconfig files (EXCLUDED_ARCHS[sdk=iphonesimulator*] = x86_64). If you see Flutter.framework/Flutter does not contain architectures "arm64 x86_64", check that this line is still there. Device and App Store builds are not affected.
05Card Studio for your team
Your operators can run Card Studio from the folder with run.sh or run.bat; nothing needs to be built. To hand out a double-click app, package it with PyInstaller on each platform and sign it: see Package it as an app.
06Check on real phones with real cards
The automated tests do not use a real NFC chip. Before you publish, run this on at least one iPhone (XS or newer) and one Android phone, with the release build and your own card domain:
- Pair a blank NTAG215 card; the first card is the Owner. Pair a second card; it joins as Backup.
- Close the app and tap the card: the app opens (iPhone needs your
apple-app-site-associationlive). - Turn on Require the card → To open the app; lock and reopen: the app asks for the card.
- Turn on For payments and swaps; send a small amount on a test or low-fee network: the card is asked.
- Make a card a Spender with a small daily limit; pay under the limit with it; then try over the limit: the app asks for the Owner or Backup card.
- Save the recovery backup on a card; check it with Check backup; read it in NFC Recovery with the PIN.
- Share the address on one card; on another phone, Send → Tap recipient's card: the address appears for confirmation.
- Program, clone and erase cards in Card Studio with the real reader; read the clone in the apps.
- CryptoCard Wallet: create a wallet on a blank card on Sepolia, receive test ETH, send it back with tap and PIN.
07Store review notes
- Explain in the review notes that the app needs an NFC card, and how the reviewer can test without one. Card screens open without a card; signing does not need a card until "Require the card" is turned on.
- Describe the card honestly in the listing: a second factor and backup carrier, not a hardware wallet.
- Apple and Google have their own rules for crypto wallet apps; check them for your region before you submit.