*-Runner.app) so it runs correctly on the MobileBoost device grid, and how to trigger and monitor runs via the API. Follow the section that matches where you want to run: physical devices or virtual devices (simulators). The two are not interchangeable - a runner built for one will not launch on the other.
You do not need to solve code signing or provisioning for our devices. Our grid re-signs the test runner with the certificate and device profile for the specific hardware it runs on.
mb_live_ API key passed as a Bearer token in the Authorization header.
Prerequisites
- Xcode (matching or newer than the OS versions you target).
- Your project has a UI Testing bundle target and a scheme that includes it in its Test action.
- You know three bundle identifiers - you’ll need them at upload time:
- App under test (must match the
.ipayou upload) - Test runner (
*-Runner) - Test bundle (
*UITests)
- App under test (must match the
1. Why physical vs. virtual matters
build-for-testing compiles your runner and its .xctest bundle for one platform slice at a time:
A simulator build links the simulator XCTest frameworks and CPU slice; a device build links the device ones. Uploading the wrong slice is the single most common failure - it will install but the test harness won’t start. If you want to run on both, build and upload two separate zips.
2. Build the runner
- Physical devices
- Virtual devices (simulators)
3. Package the runner zip
Zip from inside the Products slice folder so the.app is at the root of the archive, and use --symlinks (framework bundles contain symlinks - omitting this bloats the zip and can break the embedded frameworks).
- Physical devices
- Virtual devices (simulators)
*-Runner.app- required (containsPlugIns/*.xctest, your compiled tests)*.xctestrun- recommended. It preserves your test configuration: launch environment variables, launch arguments, the target-app reference, and skip/only test lists. Include it and your tests run exactly as configured in your scheme.
4. Verify the zip before uploading
*-Runner.app, a PlugIns/*UITests.xctest inside it, and the .xctestrun.
To sanity-check you built the device slice (not simulator) when targeting physical hardware, confirm it came out of the Debug-iphoneos folder - that folder is the source of truth for the slice.
5. Bundle-ID matching
- For physical runs, upload your enterprise-distribution
.ipaas the app under test. Do not upload the debugDebug-iphoneos/*.appproduced bybuild-for-testingas the app under test - that debug app and your test runner just need to share the bundle ID, not the binary. - If your tests target a specific bundle explicitly, use
XCUIApplication(bundleIdentifier: "<APP_UNDER_TEST_BUNDLE_ID>")and tell us that ID.
6. Code signing - what you do and don’t need to do
Physical devices: You do not need a provisioning profile that includes our devices. We re-sign the runner with our own Apple Development certificate and a profile scoped to the target hardware. To avoid any local provisioning friction, you can even build without signing:7. Optional: get your test list for sharding
If you want to split tests across devices, we can shard automatically, or you can send us the test identifiers:TargetName/ClassName/testMethod and can be passed as only-testing / skip-testing filters at launch time.
8. What to upload
You’ll end up with, per target type:Common mistakes checklist
- Built for simulator, uploaded for a physical run (or vice-versa). → Build the matching slice; upload the matching zip.
- Zipped from the wrong directory, so the
.appis nested underBuild/Products/...inside the archive. →cdinto the slice folder first; the.appmust be at the zip root. - Zipped without
--symlinks. → Frameworks break / signing fails. - App-under-test bundle ID ≠ what the tests expect. → Keep them identical; tell us the ID.
- Uploaded the debug
build-for-testingapp instead of the enterprise.ipafor physical runs. → Upload your enterprise IPA as the app under test. - Ran
testinstead ofbuild-for-testing(produces no reusable runner). → Usebuild-for-testing.
9. Trigger a test run
Once both files are uploaded, start a run with the IDs returned by the two upload calls: theapp_id from POST /uploadBuild and the test_suite_id from POST /app-automate/xcui/test-suite.
Endpoint
Optional parameters
Full request with GPS and sharding
GPS location
gpsLocation overrides the device’s location for the whole run; it is applied on each device before your first test starts and cleared automatically at teardown.
If you omit
gpsLocation, physical devices keep their real GPS and virtual devices use our neutral default. To read or change the location from inside a test, drive your app’s own location flows as usual - gpsLocation sets the starting position for the run.
Sharding
Sharding is optional. Provide ashards object to split the suite across devices and run them in parallel.
Auto-distribute - give only numberOfShards; we spread the discovered tests evenly across that many shards:
mapping array. Each entry names a shard and lists the tests it runs via strategy: "only-testing" and a values list of TargetName/ClassName[/testMethod] identifiers. numberOfShards must match the number of mapping entries.
skip-testing is also accepted per shard if you prefer to express a shard as “everything except these”. Use -showTestPlans to list valid identifiers.
Response
build_id; it identifies the run when you poll status and download results.
10. Check run status
Poll the build with thebuild_id from the launch response.
session_id.
queued, running, passed, failed, error (infrastructure/setup problem, e.g. install or signing failure), timedout.
Poll until terminal
11. Retrieve results
Once the build reaches a terminal status, fetch the machine-readable report at the build level.junit returns a standard JUnit XML your CI can ingest; json returns the same pass/fail data as structured JSON.
