VibeView runs your app on cloud iOS/tvOS simulators and Android emulators. That means the file you upload must be a simulator/emulator build — a build made for a physical device will not install. This page shows how to produce an uploadable build for each platform and framework.
Accepted formats
| Platform | Format |
|---|---|
| iOS | .app simulator bundle, compressed as .zip, .tar.gz, or .tgz |
| tvOS | .app simulator bundle, compressed as .zip, .tar.gz, or .tgz |
| Android / Android TV | .apk |
| Roku | the channel’s sideload .zip |
Uploads are limited to 500 MB per file. .ipa files are refused: an .ipa is a device build and cannot run on a simulator. See below for what to upload instead, or build it in the cloud if you have no Mac.
iOS
Why your .ipa won’t work
An .ipa (what Xcode’s Archive flow and App Store/TestFlight distribution produce) is compiled for physical iPhone hardware. Simulators need a build compiled against the simulator SDK instead. You don’t need signing certificates or provisioning profiles for a simulator build — any project that runs in the Xcode simulator can produce one.
Getting the .app from Xcode
The easiest way: run your app once on a local simulator, then grab the build Xcode already made.
-
In Xcode, select any iOS Simulator as the run destination and press Run (⌘R).
-
Find the built
.appin DerivedData:~/Library/Developer/Xcode/DerivedData/<YourApp>-<random>/Build/Products/Debug-iphonesimulator/<YourApp>.appOr locate it from within Xcode: Product → Show Build Folder in Finder, then open
Products/Debug-iphonesimulator/.
Building from the command line
xcodebuild -scheme YourApp \
-sdk iphonesimulator \
-configuration Debug \
-derivedDataPath build
The bundle lands in build/Build/Products/Debug-iphonesimulator/YourApp.app. (For workspace-based projects, add -workspace YourApp.xcworkspace.)
Architecture
VibeView’s iOS simulators run on Apple silicon, so your simulator build must include an arm64 slice. Modern Xcode includes it by default — but if your project still carries the old EXCLUDED_ARCHS = arm64 workaround for simulator builds, remove it. You can check a build with:
lipo -info YourApp.app/YourApp
The output should include arm64.
Compressing the bundle
A .app is a folder, so it must be compressed before upload — and it must sit at the top level of the archive (or in a top-level Payload/ folder), not nested inside other folders. An archive with the .app nested deeper is refused at upload.
-
Finder: right-click
YourApp.app→ Compress “YourApp”. -
Terminal:
ditto -c -k --keepParent YourApp.app YourApp.zip
Compress the .app itself — not the folder that contains it.
tvOS
Same flow as iOS, with the tvOS simulator SDK:
xcodebuild -scheme YourApp \
-sdk appletvsimulator \
-configuration Debug \
-derivedDataPath build
The bundle lands in build/Build/Products/Debug-appletvsimulator/YourApp.app. Compress and upload it the same way.
Roku
A Roku channel has no build step: what you upload is the same sideload .zip you would give a Roku’s developer web installer, with manifest, source/ and components/ (plus images/ and any data files) at the root of the archive — not inside a top-level folder.
zip -r channel.zip manifest source components images
VibeView puts the channel on the device at the start of every session and removes it when the session ends. If you keep the packaging command in vibeview.json (the development profile under platforms.roku.build.profiles, with a command and an artifact), vibeview build --platform roku packages and uploads it in one go, and vibeview dev --platform roku reloads it into a running session as you edit — see Roku in the CLI reference.
Android
Building an APK
./gradlew assembleDebug
The APK lands in app/build/outputs/apk/debug/app-debug.apk and can be uploaded as-is — debug builds are signed automatically with the debug keystore, so no release signing setup is needed.
./gradlew runs your project’s Gradle wrapper, a small script that downloads the Gradle version your project builds with. If your project has no gradlew file (Android apps exported from Google AI Studio often don’t), add one: install Gradle and run gradle wrapper --gradle-version <version> in the folder that holds settings.gradle or settings.gradle.kts. Use the version in gradle/wrapper/gradle-wrapper.properties if your project has that file; otherwise pick one your Android Gradle plugin supports (the plugin’s version is agp in gradle/libs.versions.toml, or the com.android.application version in build.gradle or build.gradle.kts). Commit the files it writes (gradlew, gradlew.bat and the gradle/wrapper/ folder) so your cloud builds can use them too.
App Bundles (.aab)
.aab files can’t be uploaded directly. Convert your bundle to a universal APK with bundletool:
bundletool build-apks --bundle=app.aab --output=app.apks --mode=universal
unzip app.apks universal.apk
Rename universal.apk as you like and upload it.
Architecture
VibeView’s Android emulators run x86_64 native code, and only some of them can also run ARM native code. If your app ships native libraries, include x86_64 so the build runs on every Android device (standard debug builds include all ABIs by default — this only matters if your build filters ABIs, e.g. via abiFilters, reactNativeArchitectures in gradle.properties, or per-ABI splits). A build with only ARM native code may run on only some devices: VibeView checks this at upload, the build’s badge says Some devices (native code), and devices that can’t run it are greyed out in the device picker. If no VibeView device can run the build’s native code, the upload is refused (see When an upload is refused). Apps with no native code run on every device.
Android TV
Upload a regular .apk. TV apps are detected automatically from the app’s manifest, and the platform shows as Android TV in your Apps list.
A Fire TV (Fire OS) .apk is an Android TV app too: upload it the same way (see Fire TV (Fire OS) apps).
Android TV devices differ in the native code they run. If your TV app ships native libraries, include both x86 and x86_64 so it runs on every Android TV device. React Native’s default reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 already does this — only trim it if you keep both. A build with only some of these (or only ARM native code) is accepted if at least one Android TV device can run it: its badge says Some devices (native code) and the other devices are greyed out in the device picker. If none can, the upload is refused. Apps with no native code run on every device.
When an upload is refused
VibeView checks every build when you upload it. If none of its devices could run the build, the upload is refused: the upload dialog shows This build can’t run on VibeView devices with the reason and how to fix it, and vibeview upload-app prints the same and exits with code 1 (with --output json it prints the reasons as JSON instead). Nothing is stored for a refused upload. If you think a build was refused by mistake, email support@vibeview.io with the file name and the reason shown, and we’ll take a look.
Device or App Store builds (iPhone, iPad, Apple TV)
Simulators only run simulator builds. A build made for a physical device or for the App Store is refused, and so is any .ipa. Make a simulator build with your framework’s tools instead, then upload the zipped .app. Framework notes has the steps for React Native, Expo, Flutter, Capacitor and Kotlin Multiplatform; for a native Xcode project, choose a simulator as the destination.
No Mac? Build it in the cloud instead. Your project must be a git repository. Run these in it, using --platform tvos for Apple TV:
npx vibeview login
npx vibeview build --cloud --platform ios --profile preview
VibeView makes a simulator build in the cloud and adds it to your app, so there’s nothing to upload. The preview profile makes a build that runs by itself, which React Native and Expo apps need. The first run suggests or asks for your build command and saves it to vibeview.json. In the upload dialog, the refusal shows these commands and a Have my coding agent build it button, which gives your agent a prompt that sets it up. See Cloud builds.
Intel-only simulator builds
VibeView’s simulators run on Apple silicon, so a simulator build containing only an Intel (x86_64) slice is refused. Build on an Apple silicon Mac, or pass ARCHS=arm64 and remove any EXCLUDED_ARCHS[sdk=iphonesimulator*]=arm64 setting. Without an Apple silicon Mac, build it in the cloud with npx vibeview build --cloud --platform ios --profile preview (--platform tvos for Apple TV), as for device builds.
App inside a folder in the archive
If the .app of an iOS or Apple TV build sits inside a folder in the archive (for example build/YourApp.app), the upload is refused, whether it is a .zip, .tar.gz or .tgz. Zip the .app folder itself (or put it in a top-level Payload/ folder) and upload again.
More than one .app in the archive
An archive that contains more than one .app at its top level (or in its top-level Payload/ folder) is refused, because VibeView can’t tell which one to install. Upload an archive that contains only YourApp.app.
Split APKs
An APK that needs other split APKs to install — for example the base APK taken out of an app bundle, or one piece of a split app — is refused. Upload a single universal APK instead. From an app bundle:
bundletool build-apks --mode=universal --bundle=app.aab --output=app.apks
unzip app.apks universal.apk
APKs without emulator native code
An APK is refused only if no VibeView device supports any of its native code (for example a build whose only native code is for an architecture no VibeView device supports). Include x86_64 (Android phones and tablets) or both x86 and x86_64 (Android TV), for example reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 in android/gradle.properties, or the matching abiFilters in build.gradle — that makes the build run on every device. See Architecture.
Unsigned APKs
Android only installs signed APKs, so an APK with no signature at all — typically Gradle’s app-release-unsigned.apk — is refused. Any key works: debug builds are signed automatically, and you can sign a build yourself with the debug keystore:
apksigner sign --ks ~/.android/debug.keystore --ks-pass pass:android app.apk
Or upload the signed build your release configuration produces instead of the unsigned one.
Invalid signatures
An APK whose signature doesn’t match its contents is refused too, because Android won’t install it. This usually means the APK was changed after it was signed, or it was copied off a device (for example a pre-installed app). Upload the APK exactly as your build produced it, or re-sign it with any key:
apksigner sign --ks ~/.android/debug.keystore --ks-pass pass:android app.apk
If you also run zipalign, run it before apksigner. Any change to the APK after signing, including aligning it, breaks the signature.
System apps
System and platform apps only install on devices signed with their manufacturer’s key, so they can’t be installed on emulators. Upload a regular app build.
Unaligned resources
An APK that targets Android 11 or later must store its resources uncompressed and 4-byte aligned, or Android refuses to install it. Build with the standard Gradle packaging, which does this for you, or run zipalign -p -f 4 in.apk out.apk before signing.
Unreadable files
If VibeView can’t read the app’s manifest (Android) or its executable (iPhone, iPad, Apple TV), the file isn’t an installable build. Re-export it from Xcode or Gradle, and upload the complete package rather than a piece of one.
No compatible device
If the app’s minimum OS version, or the device types it supports, rule out every VibeView device — for example, a build that needs a newer OS than any VibeView device runs — the upload is refused with the reason. Lower the app’s minimum OS or add support for the device types VibeView offers. If you need a newer device, contact support@vibeview.io.
Builds that run on some devices
Some builds are accepted but can only run on some VibeView devices: an iPad-only app, a build that needs a newer OS such as tvOS 18.0 or later, or a build with native code that only some devices support. The upload shows a warning saying which devices it won’t run on, and the build’s badge in Build history says so. In the device picker, devices the build can’t run on are greyed out with the reason (for example “iPad only”, “needs tvOS 18.0+” or “no compatible native code”), and you can’t start a session on them.
Framework notes
- React Native: the iOS app is a normal Xcode project — run
npx react-native run-iosonce, then grab the.appfrom DerivedData as described above. For Android,cd android && ./gradlew assembleDebug. - Builds that run on their own: a React Native or Expo build you upload to open by itself (on the Devices page, a test run or a shared link, not with
vibeview dev) must include its JavaScript bundle, so it opens without a Metro dev server. A debug build that loads its JavaScript from Metro only works in avibeview devsession; it is tagged needs Metro in Build history. - Expo: use a development or preview build with a simulator profile (
"ios": { "simulator": true }ineas.json); the build output contains the simulator.app. Expo Go itself is not your app — upload your own build. - Flutter:
flutter build ios --simulator --debugproducesbuild/ios/iphonesimulator/Runner.app. For Android,flutter build apk --debug. - Capacitor and Ionic: build the web app and run
npx cap sync, then buildios/Appwith Xcode for a simulator (as above) or run./gradlew assembleDebuginandroid/. - Kotlin Multiplatform: build the Android app module with
./gradlew :androidApp:assembleDebug(use your module’s name) and the Xcode project iniosApp/for a simulator.
Every framework above can also build in the cloud instead: see Cloud Builds.
Troubleshooting
- “An .ipa is a device (or App Store) build” — you uploaded a device build. Make a simulator build, or build it in the cloud, as Device or App Store builds describes.
- Upload refused because the
.appis inside a folder — re-compress so the.appsits at the archive’s top level (see Compressing the bundle). - “File too large” — uploads are capped at 500 MB. Debug simulator builds are usually far smaller than release archives; check you’re not zipping extra folders (like a whole DerivedData directory) alongside the
.app. - App installs but crashes immediately on iOS — check the build includes an arm64 simulator slice (see Architecture above).
- App crashes immediately on Android — if the build ships native code, check that it includes
x86_64(andx86for Android TV): without it, a device that can run ARM code runs the app through ARM translation, which may crash at launch, and the session shows a warning saying so. Also check that every native library exists for those ABIs; a slice missing some libraries crashes when the app loads one. The upload warns about an incomplete slice. If you built it yourself, run a clean build and re-upload (see Architecture above). If it’s a cloud build, a dependency ships those libraries for ARM only: update it to a version that includesx86_64, or ask its maintainer to add one. Vulkan layer libraries, which Flutter debug builds include for ARM only, don’t count: the app never loads them itself.
Next steps
Once you have your build file, head to Apps to upload it, or follow the Getting Started guide end-to-end.