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.
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.
转转QA
In the era of knowledge sharing, discover 转转QA from a new perspective.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.