Skip to main content

The .NET client

It speaks to the same tool layer as the CLI and the MCP server, by spawning mobium pipe, so the mobium binary has to be on PATH or named by MOBIUM_BIN_PATH. The quick start goes from nothing to a running script.

Install​

git clone https://github.com/mobiumdev/mobium.git
dotnet add reference mobium/clients/dotnet/Mobium/Mobium.csproj

Connect​

using var device = Device.Builder().Platform("android").App("com.android.settings").Start();
foreach (var el in device.Map()) Console.WriteLine($"{el.Ref} {el.Label}");

device below is a Mobium.Device from Device.Builder()...Start() or Device.Connect().

Every tool, by method​

MethodTool
Accessibility, SetAccessibilityapp_accessibility
Alert, AnswerAlertapp_alert
Appearanceapp_appearance
Auditapp_audit
Backgroundapp_background
Batchapp_batch
Batteryapp_battery
Biometricapp_biometric
Bootapp_boot
IncomingCallapp_call
Checkapp_check
ClearDataapp_clear_data
Clipboard, SetClipboardapp_clipboard
Contextapp_context
Contextsapp_contexts
ClearCookies, Cookies, SetCookiesapp_cookies
Crash, Crashesapp_crashes
Currentapp_current
Devicesapp_devices
AddDialogRule, ClearDialogRules, DialogRulesapp_dialogs
Doctorapp_doctor
Download, Downloads, PullPathapp_download
Dragapp_drag
Evalapp_eval
Fillapp_fill
Findapp_find
Grantapp_grant
HitTestapp_hit_test
Hookapp_hook
Installapp_install
Keyboardapp_keyboard
Launch, LaunchWithGrayBox, LaunchWithHitTestapp_launch
Appsapp_list_apps
AppLocaleapp_locale
ClearLocation, FollowGpx, FollowRoute, Location, SetLocationapp_location
ScreenLockedapp_lock
DeviceLogs, Logsapp_logs
LongPressapp_long_press
Map, MapDiffapp_map
Network, ResetNetwork, SetOffline, ShapeNetworkapp_network
Notifications, PostNotification, Shadeapp_notifications
OpenUrlapp_open_url
Orientation, OrientationLockedapp_orientation
Back, Pressapp_press
PressDragapp_press_drag
PressTapapp_press_tap
Recordapp_record
ResetPermissionsapp_reset_permissions
Revokeapp_revoke
Rotateapp_rotate
Screenapp_screen
Screenshotapp_screenshot
ScrollToapp_scroll_to
Dispose, Quit, Sessionsapp_session
Shakeapp_shake
Shutdownapp_shutdown
Smsapp_sms
Sourceapp_source
AppStateapp_state
ClearStorage, SetStorage, Storageapp_storage
Swipeapp_swipe
DoubleTap, Tap, TapFingersapp_tap
Terminateapp_terminate
Textapp_text
DeviceTimeapp_time
Timezoneapp_timezone
Trace, TraceStart, TraceStopapp_trace
Typeapp_type
Uninstallapp_uninstall
PushPath, Uploadapp_upload
WaitForapp_wait_for
Zoomapp_zoom

Connecting, ending, and errors​

Connect​

public static Device Connect()
public Device Connect()

Connects to mobium, for whichever device is running. It does not touch the device: the session there opens on the first call that needs it, or with DeviceBuilder.Start.

Connect to whichever device is running

using var dev = Device.Connect();
Console.WriteLine(dev.Current());

Connect to one device, with a named driver

using var dev = Device.Builder()
.OnDevice("emulator-5554")
.Driver("uiautomator2")
.Connect();

Builder​

public static DeviceBuilder Builder()

Configures a connection or a session.

Configure a session before starting it

var builder = Device.Builder().Platform("ios").App("com.apple.Preferences");
using var dev = builder.Start();

Start​

public Device Start()

Connects and opens the session on the device: the device-side server is started now and, given an App, it is launched and in front when this returns.

Nothing requires it — every call opens a session on first use — but it puts the slow first start (installing UiAutomator2, building WebDriverAgent on an iPhone) where it was asked for. End it with Device.Quit, or a using block, which quits:


using var device = Device.Builder().Platform("android").App("com.android.settings").Start();
device.Map();

Open a session and launch the app fresh

using var dev = Device.Builder().Platform("android").App("com.android.settings").Start();
dev.Tap("text=Network & internet");

Binary​

public DeviceBuilder Binary(string path)

Pins the mobium executable, ahead of MOBIUM_BIN_PATH and PATH.

Use a mobium binary that is not on PATH

using var dev = Device.Builder().Binary("/opt/mobium/bin/mobium").Connect();

OnDevice​

public DeviceBuilder OnDevice(string serial)

Targets one device by serial or UDID. Omit when only one is running.

Pick one of several devices

using var dev = Device.Builder().OnDevice("emulator-5554").Connect();

Driver​

public DeviceBuilder Driver(string name)
public string Driver { get; }

Chooses the driver: uiautomator2 (default on Android), uiautomator (installs nothing, slower, cannot type) or wda (iOS simulators and iPhones).

Use the zero-install Android driver

using var dev = Device.Builder().Driver("uiautomator").Connect();

CallTimeout​

public DeviceBuilder CallTimeout(TimeSpan timeout)

The longest any one call may take before the connection is given up, including the handshake. Unlimited by default, because the first session on an iPhone builds WebDriverAgent and that takes minutes.

A call that runs out ends the connection: there is one pipe, replies are told apart only by id, and the late answer to an abandoned call would be read as the answer to the next one. Every call after fails saying so; connect again. The device's session lives in the daemon, so reconnecting is cheap. Set it well above the longest Until.Timeout you use.

Bound how long any one call may take

using var dev = Device.Builder()
.CallTimeout(TimeSpan.FromMinutes(2))
.Connect();

Session​

public Session? Session { get; internal set; }
public DeviceBuilder Session(string name)
public sealed class Session

What DeviceBuilder.Start opened — device, platform, driver — or null for a device from Connect.

Name the daemon session

using var dev = Device.Builder().Session("checkout").Connect();

Platform​

public DeviceBuilder Platform(string name)
public string Platform { get; }
public string Platform { get; }

The platform for Start: android or ios. ios picks the wda driver, so none need be named.

Start a session on an iOS simulator

using var dev = Device.Builder().Platform("ios").Start();

App​

public sealed class App
public App(string id, string name, string version, bool system)
public DeviceBuilder App(string id)

One installed app.

Launch an app as the session starts

using var dev = Device.Builder().App("com.example.shop").Start();

Quit​

public void Quit()

Ends the session on the device and closes the connection. The teardown is the daemon's own: accessibility settings put back, a recording or route stopped, WebViews detached, the device-side server stopped, and the app Start launched, if any, stopped too. Quitting a session that is not open succeeds, and a second quit — a using block ending after an explicit one — does nothing. A program that exits without quitting or disposing has the sessions it started ended for it: mobium sees the client go.

End the session, putting back what it changed

var dev = Device.Builder().App("com.android.settings").Start();
try { dev.Tap("text=Display"); }
finally { dev.Quit(); }

Dispose​

public void Dispose()
public void Dispose()

Asks mobium to exit and waits for it, killing it after ten seconds. Safe to call more than once, and from another thread while a call is waiting: that call then fails, saying the connection was closed.

Let a using block quit the session

using (var dev = Device.Builder().App("com.android.settings").Start())
{
dev.Tap("text=Display");
} // quit here

Sessions​

public IList<IDictionary<string, object?>> Sessions()

The sessions open on the daemon, each with device, platform and driver.

List the sessions open on the daemon

foreach (var s in device.Sessions())
Console.WriteLine($"{s["device"]} {s["platform"]} {s["driver"]}");

Call​

public IDictionary<string, object?> Call(string tool, IDictionary<string, object?>? arguments)

Runs any tool by name, for anything this class does not wrap yet, and returns its structured answer.

Run any tool by name

var result = device.Call("app_battery", null);

Pass arguments the typed methods do not take

device.Call("app_sms", new Dictionary<string, object?> { ["text"] = "Hi", ["from"] = "5559876" });

Step​

public static IDictionary<string, object?> Step(string tool, IDictionary<string, object?>? arguments = null)

One step for Batch: a tool and the arguments it takes on its own.

Build steps for Batch

var step = Device.Step("app_tap", new Dictionary<string, object?> { ["target"] = "@e2" });
device.Batch(step, Device.Step("app_map"));

MobiumException​

public class MobiumException : Exception
public MobiumException(string message) : this(message, "")
public MobiumException(string message, string tool)

A tool reported that it could not do what was asked, or the connection to mobium failed.

Every tool failure carries a stable Code, the same in every client and on the wire (the error codes in the mobium repository's docs/guides/cli.md), and each code has a subclass — NoSuchElementException, UnsupportedException and so on — so a test catches the kind it can handle and lets the rest through. Remedy says what to do about it and Retryable whether the same call can succeed if made again. A code this client does not know, a daemon too old to send one, and a failure of the connection itself are plain MobiumExceptions. Tool names the tool that failed, or is empty for a transport-level failure.

Catch any failure

try { device.Tap("text=Nope"); }
catch (MobiumException e)
{
Console.WriteLine($"{e.Tool} {e.Code}: {e.Message} ({e.Remedy})");
}

NoSuchElementException​

public sealed class NoSuchElementException : MobiumException

A locator or ref matched nothing on screen. Worth scrolling for. Error code no_such_element.

Scroll when an element is not there

try { device.Tap("text=Continue"); }
catch (NoSuchElementException) { device.ScrollTo("text=Continue", "down"); }

AmbiguousLocatorException​

public sealed class AmbiguousLocatorException : MobiumException

A locator matched more than one element. Narrow it; Mobium never guesses. Error code ambiguous_locator.

Fall back to a ref when a locator matches more than one

try { device.Tap("text=OK"); }
catch (AmbiguousLocatorException) { device.Tap(device.Find("text=OK").First().Ref); }

TimedOutException​

public sealed class TimedOutException : MobiumException

A wait ran out. Named TimedOut so it does not collide with System.TimeoutException. Error code timeout.

Tell a timeout from other failures

try { device.WaitFor("text=Done", Until.Visible().Timeout(TimeSpan.FromSeconds(3))); }
catch (TimedOutException e) { Console.WriteLine(e.Retryable); }

UnsupportedException​

public sealed class UnsupportedException : MobiumException

This driver or platform cannot do it, and says why. Retrying cannot help. Error code unsupported.

Skip what a platform cannot do

try { device.Press("back"); }
catch (UnsupportedException e) { Console.WriteLine(e.Remedy); } // iOS has no back button

NoSuchAlertException​

public sealed class NoSuchAlertException : MobiumException

A dialog was expected and none is on screen. Error code no_such_alert.

Answer a dialog only if one is up

try { device.AnswerAlert(false); }
catch (NoSuchAlertException) { }

NoDeviceException​

public sealed class NoDeviceException : MobiumException

Nothing to drive: no device matches, or none is connected. Error code no_device.

Report that nothing is running

try { device.Map(); }
catch (NoDeviceException e) { Console.WriteLine(e.Remedy); }

DeviceNotReadyException​

public sealed class DeviceNotReadyException : MobiumException

The device is there and cannot be driven yet: locked, not trusted, Developer Mode off. Error code device_not_ready.

Unlock before launching

try { device.Launch("com.android.settings"); }
catch (DeviceNotReadyException) { device.ScreenLocked(false); device.Launch("com.android.settings"); }

NotConfirmedException​

public sealed class NotConfirmedException : MobiumException

The command reported success and reading the state back disagreed. Error code not_confirmed.

Notice an action the device did not confirm

try { device.Check("@e4"); }
catch (NotConfirmedException e) { Console.WriteLine(e.Details.Count); }

Until​

public sealed class Until

What to wait for, and how long.

Named Until rather than WaitFor because a type and a method of the same name in the same scope cannot both be reached in C#. Until.Hidden() reads better at the call site anyway.


device.WaitFor("text=Welcome"); // visible, 10s
device.WaitFor("role=progressbar", Until.Hidden()); // until it goes
device.WaitFor("@e4", Until.Text("Sent").Timeout(TimeSpan.FromSeconds(30)));

Pass a condition to WaitFor

device.WaitFor("@e4", Until.Text("Sent").Timeout(TimeSpan.FromSeconds(30)));

ExactText​

public static Until ExactText(string expected)

Wait until its text is exactly this, where Text waits for a part.

Wait for an element's whole text

device.WaitFor("testid=status", Until.ExactText("Paid"));

Count​

public static Until Count(int n)

Wait until the locator matches exactly this many elements on screen.

Wait for a list to hold three rows

device.WaitFor("role=row", Until.Count(3));

Not​

public Until Not()

The opposite of this condition: Until.Text("Sending").Not() waits for it to change.

Wait for a condition to stop holding

device.WaitFor("testid=submit", Until.Enabled().Not());

Visible​

public static Until Visible()

Wait for the element to be on screen. The default.

Wait for an element to be on screen

device.WaitFor("text=Welcome", Until.Visible());

Hidden​

public static Until Hidden()

Wait for it to go away — a spinner, say.

Wait for a spinner to go

device.WaitFor("role=progressbar", Until.Hidden());

Value​

public string Value { get; }
public static Until Value(string expected)

What a slider reads, as the app states it ("80%", "1.2"); empty for anything else.

Wait for a field's value

device.WaitFor("testid=email", Until.Value("someone@example.com"));

Enabled​

public static Until Enabled()

Wait for a control to be enabled — a Submit the app enables once a form is valid.

Wait for a button to be enabled

var button = device.WaitFor("testid=submit", Until.Enabled());

Disabled​

public bool Disabled { get; }
public static Until Disabled()

True for what the platform reports not enabled: an action on it waits, then is refused.

Wait for a button to be disabled

device.WaitFor("testid=submit", Until.Disabled());

Checked​

public bool? Checked { get; }
public static Until Checked()

A checkbox, radio or switch's state; null for anything with no such state.

Wait for a checkbox to be checked

device.WaitFor("testid=termsCheck", Until.Checked());

Unchecked​

public static Until Unchecked()

Wait for a checkbox, radio or switch to be unchecked.

Wait for a switch to be off

device.WaitFor("@e4", Until.Unchecked());

Focused​

public static Until Focused()

Wait for a field to have keyboard focus.

Wait for a field to take focus

device.WaitFor("testid=search", Until.Focused());

Timeout​

public Until Timeout(TimeSpan d)

How long before giving up. Ten seconds by default, two minutes at most.

Wait longer than the ten-second default

device.WaitFor("text=Ready", Until.Visible().Timeout(TimeSpan.FromSeconds(30)));

Types​

TypeWhat it is
AppOne installed app.
BoundsAn on-screen rectangle in device pixels, on both platforms.
DeviceDrives mobile apps on Android emulators, Android phones, iOS simulators and iPhones.
DeviceBuilderOptions for Device.Connect().
DeviceInfoOne attached device or simulator.
ElementOne actionable thing on screen.
LocationWhere a device believes it is.
MobiumExceptionA tool reported that it could not do what was asked, or the connection to mobium failed.
NoDeviceExceptionNothing to drive: no device matches, or none is connected.
DeviceNotReadyExceptionThe device is there and cannot be driven yet: locked, not trusted, Developer Mode off.
ToolchainMissingExceptionSomething on this machine is missing: adb, Xcode, a signing certificate.
NoSuchElementExceptionA locator or ref matched nothing on screen.
AmbiguousLocatorExceptionA locator matched more than one element.
ElementNotReachableExceptionThe element was found and cannot be touched where it is, usually off screen.
NoSuchContextExceptionA WebView context that is not there.
NoSuchAlertExceptionA dialog was expected and none is on screen.
UnsupportedExceptionThis driver or platform cannot do it, and says why.
NotConfirmedExceptionThe command reported success and reading the state back disagreed.
TimedOutExceptionA wait ran out.
InvalidArgumentExceptionThe request itself is wrong.
DeviceServerExceptionThe device side failed (a device server, or adb, simctl, devicectl, lockdown); a server's W3C code is Details["w3c"].
InternalExceptionA bug in Mobium.
SessionWhat DeviceBuilder.Start opened: the device and how it is driven.
UntilWhat to wait for, and how long.