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.
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

PythoniOS 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

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.