App Store & Google Play
By link and QR code an app opens for anyone — through the le.codes app or in the browser — with no build and no store (see Publishing). Your own app in a store — under your name, with your icon, no intermediary — is the second path.
Rather not deal with Xcode and keys — write to support. For now we do App Store and Google Play releases by hand on request: you send the project, the name, the icon and your developer-account details — we build, sign and upload. Publishing from the platform itself comes later. Email support@le.codes, Telegram @lecodes_bot.
Want to do it yourself — lecodes app does it: generates a native shell, builds it and sends it to App Store Connect. Below, honestly: what you need, what the CLI does, and what stays with you.
What you need
| iOS | Android | |
|---|---|---|
| Account | Apple Developer Program (paid, yearly) | Google Play Console (one-time fee) |
| Machine | A Mac with Xcode — an iOS app cannot be built or signed otherwise | Windows, macOS or Linux: a JDK and the Android SDK (easiest with Android Studio) |
| Signing | automatic through Xcode, by team id | an upload key — the CLI creates and keeps it |
| Upload | lecodes app upload ios — straight into App Store Connect | by hand in the Play Console: the .aab the CLI built |
le.codes has no cloud build yet: without a Mac there is no way into the App Store. That is the path's main limitation, and better known up front.
Step 1 — the shell
lecodes app init ios --team ABCDE12345 # an Xcode project in ./ios, bundle id com.example.<slug> by default
lecodes app init ios --bundle-id com.acme.game # your own bundle id
lecodes app init android --app-id com.acme.game # a Gradle project in ./androidThe shell is an ordinary Xcode or Gradle project that runs the compiled bundle offline: the app works without a network and without the platform. ios/ and android/ are never pushed to le.codes and live in your git. The native SDK version is pinned in app.json (ios.sdk, android.sdk) so builds stay reproducible; the SDK variant — core, 2d, 3d or full — is picked from what the bundle really uses.
The icon is a square image of 1024 px or more under app.json → icon; the CLI renders every size on each sync. Native plugins (camera, QR scanner, map, geolocation, push) are added with lecodes app plugin add <id> and vendored into the shell with pinned versions.
Step 2 — sync and preflight
lecodes app sync # bundle + assets + SDK variant + plugins → into the shell; your Swift/Kotlin files are never touched
lecodes app doctor # toolchain, signing, icons, keys — fatal checks fail the command, soft ones print a hint
lecodes app open ios # open the project in Xcode / Android Studio when you need tosync runs by itself before every build; on its own it is for when you edit the shell in the IDE. Run doctor before the first release: it checks everything that usually breaks at upload time.
iOS: build and upload
lecodes app build ios # xcodebuild archive + .ipa export (app-store-connect method) into ios/build/export/
lecodes app upload ios \
--key-path ~/AuthKey_ABC123.p8 --key-id ABC123 --issuer-id 1234-… # an App Store Connect API key — once, then rememberedThe build signs automatically by the team id from app init. upload ios sends the archive to App Store Connect and bumps ios.buildNumber in app.json — a build number is consumed by the upload. From there it is like any app: TestFlight, the App Store Connect page, submission for review. --method ad-hoc builds an .ipa for your own devices outside the store.
OTA code updates: "update": true in app.json — the iOS shell fetches the new JS on launch, so a small fix needs no store release. Not on Android yet.
Android: key, build, Play Console
lecodes app keystore create --generate # an upload key: path and passwords go to the project .env, the metadata to app.json
lecodes app keystore use ~/keys/upload.keystore # or your existing one
lecodes app keystore info # the alias and SHA-1 / SHA-256 fingerprints Play and Firebase ask for
lecodes app build android # gradlew assembleRelease + bundleRelease → dist/<name>-<version>-<code>.apk and .aabThe CLI refuses a release without a key — --allow-unsigned is for throwaway builds only. With Play App Signing (the default for new apps) your key is the upload key: Google keeps the signing key, and a lost upload key can be reset through Play support. Back it up anyway.
There is no Play upload in the CLI: you submit the .aab from dist/ in the Play Console yourself — creating the app, test tracks, the store listing and review happen there. Gradle finds the Android SDK in android/local.properties or via ANDROID_HOME.
Updates
A new app version is a new bundle version in the shell: edit the project, raise the version and build code in app.json, lecodes app build — and back to the store. lecodes app update moves the SDK and plugin pins to the newest releases and re-syncs the shell (--check only reports what is newer); lecodes update does this among other things.
The link and QR code keep working meanwhile: an update through them is free and skips review. Many keep both paths: the store for people who search there, the link for everyone else.
Honest limits
- No Mac and Xcode — no iOS build; there is no cloud build.
- Google Play upload is by hand in the Play Console; the CLI prepares the signed .aab.
- OTA JS updates are iOS-only.
- A native plugin is a separate repository with native code (
github:owner/repo@tag), not a file in the project; review a third-party plugin before shipping. - Passing store review, screenshots and listing copy are your part of the work, as with any other tool.
None of this stops you from shipping today by link and QR code and listing the app in le.store — and going to a store when there is a reason to.