guides
Integration guides.
AppScreen does the design; fastlane (or your own scripts) does the upload. Pick the export layout that matches your project, drop the rendered bundle into your repo, and submit.
Choose your project layout
The studio's export layout picker writes the rendered bundle in a tree that drops directly into your repo. One row below per supported project shape:
| Layout | iOS path | Android path |
|---|---|---|
| fastlane | fastlane/ | fastlane/ |
| flutter | ios/fastlane/ | android/fastlane/ |
| react-native | ios/fastlane/ | android/fastlane/ |
| capacitor | ios/App/fastlane/ | android/fastlane/ |
| kmm | iosApp/fastlane/ | androidApp/fastlane/ |
The default (raw / flat) layout produces theme/size/lang/n.png and is intended for web pages, social, and decks rather than store submission.
Flutter
Flutter projects keep iOS and Android in subdirs, each with its own fastlane/.
cd /path/to/your_flutter_app unzip ~/Downloads/appscreen-*.zip
Files land at:
ios/fastlane/screenshots/<locale>/iPhone69/01.png … android/fastlane/metadata/android/<locale>/images/phoneScreenshots/01.png …
Append a screenshots lane to ios/fastlane/Fastfile and android/fastlane/Fastfile (snippets in the Fastfile lanes section), then run:
cd ios && bundle exec fastlane screenshots && cd .. cd android && bundle exec fastlane screenshots && cd ..
React Native (incl. Expo bare)
Same layout as Flutter — separate ios/ + android/ subprojects each with their own fastlane folder. Expo bare-workflow projects follow the same shape after expo prebuild.
cd /path/to/your_rn_app unzip ~/Downloads/appscreen-*.zip
cd ios && bundle exec fastlane screenshots && cd .. cd android && bundle exec fastlane screenshots && cd ..
Capacitor / Ionic
Capacitor's iOS xcworkspace lives under ios/App/. Files land at ios/App/fastlane/ + android/fastlane/.
cd /path/to/your_capacitor_app unzip ~/Downloads/appscreen-*.zip
cd ios/App && bundle exec fastlane screenshots && cd ../.. cd android && bundle exec fastlane screenshots && cd ..
Kotlin Multiplatform
KMM projects use iosApp/ + androidApp/ modules.
cd /path/to/your_kmm_app unzip ~/Downloads/appscreen-*.zip
cd iosApp && bundle exec fastlane screenshots && cd .. cd androidApp && bundle exec fastlane screenshots && cd ..
Bare iOS or Android (single fastlane root)
For a repo with fastlane initialised at the project root — either an Xcode-only iOS project or a Gradle-only Android project. The bundle's fastlane/ folder merges with the existing one.
cd /path/to/your_project unzip ~/Downloads/appscreen-*.zip
# iOS bundle exec fastlane screenshots # Android bundle exec fastlane screenshots
Fastlane authentication
One-time setup of an App Store Connect API key (recommended over login + 2FA prompts) and, for Play Store, a Google service account.
App Store Connect API key
- App Store Connect → Users and Access → Integrations → App Store Connect API → Generate API Key. Role: Marketing (sufficient for screenshots; least privilege).
- Download the
.p8private key. Note the Key ID and Issuer ID. - Save the key to
~/.appstore/AuthKey.p8and create an~/.appstore/api_key.json:
{
"key_id": "ABC1234XYZ",
"issuer_id": "00000000-0000-0000-0000-000000000000",
"key_filepath": "~/.appstore/AuthKey.p8"
}Google Play service account
- Google Cloud Console → create a service account in the project linked to your Play account. Grant role Service Account User.
- Generate a JSON key for that service account. Save as
~/.playstore/service-account.json(and gitignore it if committing fastlane config). - Play Console → Users and permissions → invite the service-account email. Grant Manage store presence.
Fastfile lanes
Append these lanes to the appropriate Fastfile (see the layout table for the path).
iOS
# inside platform :ios do … end
desc "Upload screenshots only"
lane :screenshots do |options|
project_root = File.expand_path("..", __dir__)
api_key_path = options[:api_key_path] ||
ENV["APP_STORE_CONNECT_API_KEY_PATH"] ||
File.expand_path("~/.appstore/api_key.json")
api_key = app_store_connect_api_key(
key_id: JSON.parse(File.read(api_key_path))["key_id"],
issuer_id: JSON.parse(File.read(api_key_path))["issuer_id"],
key_filepath: JSON.parse(File.read(api_key_path))["key_filepath"],
duration: 1200,
)
deliver(
api_key: api_key,
skip_binary_upload: true,
skip_metadata: true,
skip_screenshots: false,
overwrite_screenshots: true,
force: true,
screenshots_path: File.join(project_root, "fastlane", "screenshots"),
)
endAndroid
# inside platform :android do … end
desc "Upload screenshots only"
lane :screenshots do
upload_to_play_store(
skip_upload_apk: true,
skip_upload_aab: true,
skip_upload_metadata: true,
skip_upload_changelogs: true,
skip_upload_images: false,
skip_upload_screenshots: false,
metadata_path: File.expand_path("./metadata", __dir__),
)
endCI integration
The bundle is just files; any CI flow that already runs fastlane can pull it during a release build. Two common patterns:
- Manual checkout: download the ZIP from AppScreen, commit
fastlane/screenshotsandfastlane/metadata, run fastlane in CI on tag push. - Stored in CI artifact: upload the ZIP to your CI artifact store; expand into
fastlane/before the lane runs. Useful if you don't want generated PNGs in version control.
Example GitHub Actions step:
- name: Unzip AppScreen bundle
run: |
unzip -q appscreen-bundle.zip -d .
- name: Publish screenshots
env:
ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }}
ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }}
ASC_KEY_PATH: ${{ secrets.ASC_KEY_PATH }}
run: bundle exec fastlane screenshotsLocalization
Render every language you support inside one bundle (each maps to the correct en-US, es-ES, de-DE folder). Fastlane pushes them all in one run, but each locale must already be added to the editable version in App Store Connect / Play Console — otherwise it gets silently skipped.
Common errors
- Asset failed validation on iOS: typically a non-RGB / alpha-channel PNG. AppScreen's fastlane export already alpha-flattens for store layouts, so this only happens if you swapped a manual file in. Re-render the bundle.
- 401 INVALID_PROVIDER from supply: the service account isn't invited in Play Console yet, or its role is too narrow. Add Manage store presence.
- Wrong device folder: deliver expects
iPhone69/iPhone67/iPhone65. The export uses these names already. - Empty Media Manager after a successful upload: you're probably viewing the inflight (locked) version. Open the version in Prepare for Submission; that's where deliver writes.
Stuck on a step? The studio FAQ covers the most common questions.
Open studio