Mobile Development 18 min read

Using tidevice for iOS Automation without a Mac: Features, Installation, and Integration Guide

This article introduces tidevice, an open‑source tool that enables iOS automation on Windows, Linux, and macOS by providing device information, app management, WebDriverAgent control, performance data collection, and various command‑line and Python APIs, along with detailed installation steps and integration advice.

转转QA
转转QA
转转QA
Using tidevice for iOS Automation without a Mac: Features, Installation, and Integration Guide

Introduction

iOS automation has traditionally required a macOS environment because Xcode must compile and install WebDriverAgent (WDA) on the device. This limitation forces non‑Mac users to acquire Mac hardware, leading to under‑utilized resources and high costs for cloud testing platforms.

Alibaba’s open‑source project tidevice ( https://github.com/alibaba/taobao-iphone-device ) removes the macOS dependency, allowing developers to perform iOS automation from any platform.

What tidevice Can Do

Retrieve device information.

Install, uninstall, launch, stop, and query apps, including listing installed apps.

Start WebDriverAgent without Xcode (cross‑platform).

Run XCTest UI tests.

Collect performance data such as FPS.

Take screenshots and fetch device logs.

Core Principle

usbmux Communication Protocol

tidevice uses the usbmux protocol to communicate between a host (Mac/Windows/Linux) and an iOS device. macOS provides the native usbmuxd service; on Windows and Linux, open‑source implementations are used, sometimes requiring AppleApplicationSupport and AppleMobileDeviceSupport.

The protocol is essentially a TCP‑like socket over USB, which tidevice simulates with Python to implement all its features.

Installation

Python 3.6+ is required.

Install tidevice: pip3 install -U "tidevice[openssl]" (recommended) or pip3 install -U tidevice (without pairing support).

Install usbmuxd: macOS: built‑in at /var/run/usbmux . Linux/Windows: follow the instructions at https://github.com/alibaba/taobao-iphone-device/issues/7 .

Command‑Line and Python Interfaces

All tidevice commands are defined in tidevice.__main__ . The following sections show representative commands and their equivalent Python code.

Device Management

List Connected Devices

Command: tidevice list

Python: from tidevice import Usbmux print(Usbmux().device_list())

Watch Device Connection Events

Command: tidevice watch

Python: from tidevice import Usbmux for info in Usbmux().watch_device(): print(info)

Note: Usbmux().watch_device() returns a generator that blocks the main thread; using a separate process is recommended.

Wait for Any Device

Command: tidevice wait‑for‑device

Python: from tidevice import Usbmux for info in Usbmux().watch_device(): if info["MessageType"] == "Attached": break

Wait for a Specific Device

Command: tidevice -u $UDID wait‑for‑device

Python: from tidevice import Usbmux for info in Usbmux().watch_device(): if info["MessageType"] != "Attached": continue udid = info["Properties"]["SerialNumber"] if udid == "$UDID": break

Print Device Logs

Command: tidevice -u $UDID syslog

Python: from tidevice import Device d = Device("udid") d.start_service("com.apple.syslog_relay") while True: print(s.recv().decode("utf-8"))

App Management

Install an App

Command: tidevice --udid $UDID install example.ipa

Python: from tidevice import Device Device("udid").app_install("example.ipa")

Uninstall an App

Command: tidevice --udid $UDID uninstall com.example.demo

Python: from tidevice import Device Device("udid").app_uninstall("com.example.demo")

Launch an App

Command: tidedevice --udid $UDID launch com.example.demo

Python: from tidevice import Device pid = Device("udid").app_start("com.example.demo") # Force restart pid = Device("udid").app_start("com.example.demo", kill_running=True)

Stop an App

Command: tidevice --udid $UDID kill com.example.demo

Python: from tidevice import Device Device("udid").app_stop(pid_or_name)

List Installed Apps

Command: tidevice --udid $UDID applist

Python: from tidevice import Device instruments = Device("udid").connect_instruments() apps = instruments.app_list() user_apps = [app for app in apps if app["Type"] == "User"]

Get App Information

Command: tidevice appinfo com.example.demo

Python: from tidevice import Device Device("udid").installation.lookup("com.example.demo")

Parse IPA (URL or File)

Command: tidevice parse ipa_url

Python (URL): import httpio from tidevice._ipautil import IPAReader fp = httpio.open(url, block_size=1) ir = IPAReader(fp) ir.get_infoplist()

Python (File): from tidevice._ipautil import IPAReader fp = open("path/to.ipa", "rb") ir = IPAReader(fp) ir.get_infoplist()

Automation Execution

Run XCTest

Command: tidevice xctest -B com.facebook.wda.WebDriverAgent.Runner

Python: from tidevice import Device Device("udid").xctest("com.facebook.wda.WebDriverAgent.Runner")

Running XCTest blocks subsequent operations; using a separate process is advised.

Run XCTest with Custom Port and Debug Logging

Command: idevice XCTestCase -B com.facebook.wda.WebDriverAgent.Runner -e USB_PORT:8200 --debug

Python: from tidevice import Device import logging logger = logging.getLogger("tidevice.xctest") Device("udid").xctest("com.facebook.wda.WebDriverAgent.Runner", log=logger, evn={"USB_PORT": 8200})

Relay (Port Forwarding)

Command: tidedevice relay 8100 8100

Python: from tidevice import Device from tidevice._relay import relay d = Device("udid") relay(d, 8100, 8100)

Debug mode (hex dump): tidedevice relay -x 8100 8100

Python debug: relay(d, 8100, 8100, debug=True)

WDA Proxy

Command: tidedevice wdaproxy -B com.facebook.wda.WebDriverAgent.Runner --port 8200

Python (method 1): from tidevice._wdaproxy import WDAService from tidevice import Device d = Device("udid") serv = WDAService(d, "com.facebook.wda.WebDriverAgent.Runner") # start subprocess that runs relay 8200->8100 # then serv.start() … serv.stop()

Python (method 2 – combine xctest and relay with multiprocessing): from tidevice import Device from tidevice._relay import relay from multiprocessing import Process d = Device("udid") p1 = Process(target=d.xctest, args=("com.facebook.wda.WebDriverAgent.Runner", None, None, {"USB_PORT": 8200})) p2 = Process(target=relay, args=(d, 8200, 8100, False)) p1.start(); p2.start() # … later terminate both processes

Device Information

Basic Info

Command: tidedevice info

Python: from tidevice import Device Device("udid").device_info()

Battery Info

Command: tidedevice info --domain com.apple.mobile.battery --json

Python: from tidevice import Device import json info = Device("udid").device_info("com.apple.mobile.battery") print(json.dumps(info))

System Info

Command: tidedevice sysinfo

Python: from tidevice import Device Device("udid").instruments.system_info()

FPS Data

Command: tidedevice dumpsfps

Python: from tidevice import Device d = Device("udid") for data in d.instruments.iter_opengl_data(): if isinstance(data, str): continue print(data["CoreAnimationFranesPerSecond"])

Device Operations

Screenshot

Command: tidedevice screenshot

Python (save to timestamped file): from tidevice import Device import time filename = "screenshot_" + str(time.time()) + ".jpg" Device("udid").screenshot().conver("RGB").save(filename)

Shutdown / Reboot

Command: tidedevice shutdown / tidedevice reboot

Python: from tidevice import Device Device("udid").shutdown() Device("udid").reboot()

File Sync

Command: tidedevice -u $UDID fsync

Python: from tidevice import Device Device("udid").sync

App File Sync

Command: tidedevice -u $UDID fsync -B com.example.app

Python: from tidevice import Device Device("udid").app_sync("com.example.app")

Pair Device

Command: tidedevice -u $UDID pair

Python: from tidevice import Device Device("udid").pair()

Integration Guidance

The typical workflow uses Appium as the test framework. Before launching the Appium driver, tidevice can start WDA and forward ports (wdaproxy). The local port must be recorded and injected into the driver capabilities as webDriverAgentUrl . After tests finish, ensure tidevice processes are terminated.

Recommendations

If you still have a macOS pipeline, keep it as a fallback because tidevice’s wdaproxy may occasionally experience communication issues.

When integrating on non‑macOS platforms, add logic to detect the host OS and choose the appropriate tidevice commands.

Conclusion

tidevice frees iOS automation from the macOS constraint, opening new possibilities for cross‑platform test infrastructures. Windows users can start WDA via wdaproxy and drive tests with Appium. Linux servers can host remote iOS automation using tidevice. Performance data collection is possible on macOS, Windows, and Linux. Because tidevice only uses a subset of the underlying usbmux code, developers can extend it further by reading the source. Thanks to the Alibaba team for open‑sourcing tidevice; we hope this guide helps you get started. Give it a try, integrate it, and explore the source! If you liked this article, please follow, share, like, comment, or tip.
PythonMobile TestingiOS Automationdevice managementtidevice
转转QA
Written by

转转QA

In the era of knowledge sharing, discover 转转QA from a new perspective.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.