Skip to main content

Defining your app

Model your application under test as a singleton extending AndroidApp, IosApp, or CrossPlatformApp.

Each class supports two launch modes - by identifier (preinstalled app) and by app path (fresh install) - plus an escape hatch via a custom driverFactory lambda. See Appium launch modes for details on how these map to Appium capabilities.

Android

Preinstalled app (launch by package)

The most common case. Provide appPackage and appActivity; the driver factory is derived automatically.

object MyAndroidApp : AndroidApp(
appPackage = "com.example.app",
appActivity = ".MainActivity",
)

appPackage is stored as a property and can be referenced in screen locators:

private val products by xpaths(
"""//*[@resource-id="${MyAndroidApp.appPackage}:id/productRV"]/android.view.ViewGroup""",
)

Install from APK/AAB

Provide appPath; Appium installs the binary and launches it automatically. You can optionally include appPackage if your screens need it for locators or deep links - but do not include appActivity, since Appium determines the launch activity from the APK manifest.

object MyAndroidApp : AndroidApp(
appPath = "/path/to/app.apk",
)

With appPackage for locators:

object MyAndroidApp : AndroidApp(
appPath = "/path/to/app.apk",
appPackage = "com.example.app",
)

iOS

Preinstalled app (launch by bundle ID)

object MyIosApp : IosApp(
bundleId = "com.example.ios",
)

bundleId is stored as a property and can be referenced in screen locators.

Install from .app/IPA

Provide appPath; Appium installs the binary and derives the bundle ID from it automatically. Do not include bundleId when using appPath - Appium ignores it in install mode.

object MyIosApp : IosApp(
appPath = "/path/to/app.ipa",
)

Cross-platform

CrossPlatformApp composes an AndroidApp and an IosApp:

object MyApp : CrossPlatformApp(
android = MyAndroidApp,
ios = MyIosApp,
)

Or define the platform apps inline:

object MyApp : CrossPlatformApp(
android = object : AndroidApp(
appPackage = "com.example.app",
appActivity = ".MainActivity",
) {},
ios = object : IosApp(
bundleId = "com.example.ios",
) {},
)

Properties like appPackage and bundleId are available through delegation:

MyApp.appPackage   // delegates to MyApp.android.appPackage
MyApp.bundleId // delegates to MyApp.ios.bundleId

Custom driver factory

Since the driver factory is just a () -> AppiumDriver function, you can configure the driver however you need. Pass it as the driverFactory parameter alongside any metadata properties your screens require:

object MyAndroidApp : AndroidApp(
appPackage = "com.example.app",
driverFactory = {
val options = UiAutomator2Options().apply {
setAppPackage("com.example.app")
setAppActivity(".MainActivity")
setNewCommandTimeout(Duration.ofSeconds(60))
}
AndroidDriver(URI("http://127.0.0.1:4723").toURL(), options)
},
)

Appium launch modes

Appium treats launch by identifier and install from path as two distinct execution modes. Kolibrium enforces this distinction at construction time.

Android

ParameterLaunch modeWhat Appium does
appPackage + appActivityAttachLaunches an already-installed app by package and activity
appPathInstallInstalls the APK/AAB and launches it; determines activity from the manifest
appPath + appPackageInstallSame as above; appPackage is stored for locators/deep links but not sent to Appium for launching

⚠️ appPath + appActivity is rejected. When installing from an APK, Appium ignores appActivity - providing it suggests a misconfiguration. If you need appPackage for locators, provide it without appActivity.

iOS

ParameterLaunch modeWhat Appium does
bundleIdAttachLaunches an already-installed app by bundle identifier
appPathInstallInstalls the .app/IPA and launches it; derives the bundle ID from the binary

⚠️ appPath + bundleId is rejected. When installing from an app path, Appium derives the bundle ID itself - providing both suggests a misconfiguration.

Rule of thumb

  • Fresh install (CI, new build) → use appPath
  • Reuse installed app (faster local runs) → use appPackage/appActivity or bundleId
  • Never mix both modes - they represent fundamentally different Appium execution paths