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
| Parameter | Launch mode | What Appium does |
|---|---|---|
appPackage + appActivity | Attach | Launches an already-installed app by package and activity |
appPath | Install | Installs the APK/AAB and launches it; determines activity from the manifest |
appPath + appPackage | Install | Same as above; appPackage is stored for locators/deep links but not sent to Appium for launching |
⚠️
appPath+appActivityis rejected. When installing from an APK, Appium ignoresappActivity- providing it suggests a misconfiguration. If you needappPackagefor locators, provide it withoutappActivity.
iOS
| Parameter | Launch mode | What Appium does |
|---|---|---|
bundleId | Attach | Launches an already-installed app by bundle identifier |
appPath | Install | Installs the .app/IPA and launches it; derives the bundle ID from the binary |
⚠️
appPath+bundleIdis 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/appActivityorbundleId - Never mix both modes - they represent fundamentally different Appium execution paths