TouchAction vs the W3C Actions API in Appium
if you have an old Appium suite and it suddenly throws errors on TouchAction or MultiAction, you are not alone. this is the question behind stackoverflow.com/questions/53173642: what replaces TouchAction, and how do I rewrite my swipes and taps? the short answer is the W3C Actions API (pointer input sources, performed as sequences), or, for common gestures on Android, the mobile: gesture commands in the UiAutomator2 driver. this article walks through why the old API went away and how to translate it line by line.
a note on the template this comparison sits in. the site’s vs format was built around proxy vendors, so the frontmatter says “Vendor A” and “Vendor B” and the category field says proxies. here Vendor A is TouchAction (with MultiAction) and Vendor B is the W3C Actions API. the head-to-head axes below (IP pool size, geo coverage and so on) are proxy terms. I kept the headings and tell you honestly where an axis does not apply, and where it maps onto something real, like geo testing over mobile IPs.
my verdict per use case, up front. for any new code, or any old code you have to touch, Vendor B (W3C Actions) wins. for a one-off swipe or scroll on Android, the mobile: gesture commands are shorter than either. TouchAction only wins if you are frozen on an old Appium 1.x server and an old client, and even then you are borrowing time. I am Xavier Fok, I run Android automation out of Singapore, and I have migrated enough of these suites to have opinions on the gotchas.
TL;DR comparison table
| Vendor A: TouchAction / MultiAction | Vendor B: W3C Actions API | |
|---|---|---|
| Pricing | free, open source client classes | free, part of the W3C WebDriver standard |
| Origin | old Selenium/Appium JSON Wire style touch endpoints | W3C WebDriver “Actions” with pointer input sources |
| Status (as of October 2026) | removed or unsupported in current Appium 2 clients and drivers, check your client changelog | the supported path in Appium 2 and Selenium 4 |
| Coordinates | moveTo is often an offset from the last point |
absolute viewport coordinates unless you pass an origin element |
| Multi-finger | MultiAction combining several TouchActions |
several pointer input sources in one perform |
| Support | no new fixes, community answers are mostly outdated | Selenium docs, Appium docs, active drivers |
| Target user | maintainers of legacy suites on Appium 1.x | everyone writing or migrating tests now |
Vendor A at a glance
TouchAction was the original Appium gesture builder. you chained press, waitAction, moveTo, longPress, tap and release, then called perform(). a swipe looked like press at one point, wait a few hundred milliseconds, moveTo the target, release, perform. MultiAction took several TouchActions and ran them together, which is how people did pinch and zoom.
it was popular because it read like a sentence and worked across iOS and Android. it came from the older JSON Wire protocol era, before the W3C WebDriver standard defined its own input model. when Appium moved to the W3C protocol and Appium 2 dropped the legacy protocol layer, TouchAction had no standard home to live in. the client libraries deprecated it first and then removed it. exact versions differ by language client, so check the changelog of the Java, Python or other client you use rather than trusting a blog post, including this one.
the practical effect as of October 2026: code that imports TouchAction either fails to import, fails at runtime with an unsupported command error, or works only because you pinned very old versions. none of those is a place I want a test suite to sit.
Vendor B at a glance
the W3C Actions API is part of the W3C WebDriver specification. instead of one chain of verbs, you build input sources. a touch screen is a pointer input source of type touch. each source gets a sequence of actions: pointer move, pointer down, pause, pointer up. the server runs the sequences in lockstep, tick by tick, which is exactly what makes multi-finger gestures well defined. the Selenium documentation on the Actions API describes the model, and Appium clients reuse Selenium’s builders.
two things are different from TouchAction and they cause most migration bugs. first, pointer moves use a duration, so the swipe speed lives in the move, not in a separate wait. second, coordinates are absolute in the viewport by default, where TouchAction moveTo was commonly written as a relative offset. I come back to that below.
on Android specifically, the UiAutomator2 driver also ships mobile: commands such as mobile: clickGesture, mobile: swipeGesture, mobile: scrollGesture and mobile: pinchOpenGesture. these skip the sequence building and are often the shortest rewrite. they are Android-only, so if you share code with iOS you may still want W3C Actions.
head-to-head
IP pool size
this axis does not apply to a gesture API. there is no pool. the nearest honest mapping is gesture surface: how many gesture shapes each approach can express. TouchAction covered single-finger chains and combined multi-finger via MultiAction. W3C Actions covers the same and more, because a sequence can mix pointer types and arbitrary pauses. for the proxy side of testing, see the geo coverage section.
rotation control
again not literal. the nearest thing is how much control you have over timing and repetition. in TouchAction you inserted waitAction and hoped. in W3C Actions each move carries its own duration and you can add explicit pause actions, so a slow drag and a flick are different durations on the same move. you can also reuse the same sequence object in a loop. that is the control I wanted all along.
geo coverage
gestures have no geography, but a lot of mobile tests do. when the app under test behaves differently by country, the gesture code is not the variable, the network is. I covered that side in testing apps on real carrier networks. for our own products, Singapore Mobile Proxy sells real Singapore mobile IPs with sticky sessions, and it is Singapore only. if you need other countries, you need a different provider.
connection success rate
I am not going to invent a success percentage. I have no benchmark I can publish for this, and there is none worth citing. what I can say qualitatively: on Appium 2 with a current UiAutomator2 driver, W3C Actions are the path the driver tests against, so they are the path that works. TouchAction on a modern server is a path that may simply not exist. the failure mode is loud (an error), not subtle, which at least makes the migration obvious.
speed
I have not measured TouchAction against W3C Actions and will not claim a number. what I can say from reading the protocol: a W3C perform sends the whole sequence in one request, and the server runs the ticks. TouchAction chains were also sent as one batch in most clients, so I would not expect a dramatic difference. the big speed lever on Android is not the API, it is whether you use mobile: swipeGesture instead of building a sequence yourself. measure it on your own device before you decide.
pricing per GB
there is no per-GB price for either. both are free. the cost is engineering time: rewriting old gesture helpers, re-checking coordinates, and re-running the suite. if you have a few helpers (swipe, scroll, long press, tap at point), the migration is an afternoon. if gestures are scattered through hundreds of tests, centralise them into a helper first and then migrate the helper once.
session persistence
for a gesture API, the closest meaning is state between actions. W3C Actions have a clear state model: after perform, input state stays in the session until you call release actions (the DELETE /session/{id}/actions endpoint, exposed as releaseActions or release_actions depending on the client). if a swipe leaves a finger “down” because a sequence ended early, the next gesture can misbehave. TouchAction hid this state from you, which is partly why odd bugs lingered.
on the proxy side, sticky sessions are what keep a mobile IP stable across a test run, so a login flow does not get a new IP halfway. Singapore Mobile Proxy has them, Singapore only.
concurrent connections
for gestures, the nearest idea is concurrent fingers. MultiAction ran several TouchActions together. in W3C Actions you create one pointer input source per finger, give each its own id (finger1, finger2), and pass both sequences to a single perform. the server aligns them by tick, so a pinch is two moves of equal duration starting at the same tick.
for running many phones at once, the limit is your host, not the API. I wrote up those limits in host limits when running many Android phones.
translating old code
here is the part the Stack Overflow question is really after. I will show the classic swipe, then tap and long press, then pinch.
the old swipe
# old, do not use on Appium 2
TouchAction(driver) \
.press(x=500, y=1600) \
.wait_action(500) \
.move_to(x=500, y=600) \
.release() \
.perform()
the new swipe in Python
from selenium.webdriver.common.actions import interaction
from selenium.webdriver.common.actions.action_builder import ActionBuilder
from selenium.webdriver.common.actions.pointer_input import PointerInput
def swipe(driver, x1, y1, x2, y2, duration_ms=500):
finger = PointerInput(interaction.POINTER_TOUCH, "finger")
actions = ActionBuilder(driver, mouse=finger)
actions.pointer_action.move_to_location(x1, y1)
actions.pointer_action.pointer_down()
actions.pointer_action.pause(0.1)
actions.pointer_action.move_to_location(x2, y2) # see duration note below
actions.pointer_action.release()
actions.perform()
watch the duration. the default pointer move duration in the Python builder is short, so a swipe may behave like a flick. if you need a slow drag, set the move duration on your PointerInput (the constructor and builder expose a duration setting in current Selenium releases) or add pauses. check the Selenium version you have installed, since this builder API has changed between 4.x releases.
the new swipe in Java
PointerInput finger = new PointerInput(PointerInput.Kind.TOUCH, "finger");
Sequence swipe = new Sequence(finger, 0);
swipe.addAction(finger.createPointerMove(Duration.ZERO,
PointerInput.Origin.viewport(), 500, 1600));
swipe.addAction(finger.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
swipe.addAction(finger.createPointerMove(Duration.ofMillis(500),
PointerInput.Origin.viewport(), 500, 600));
swipe.addAction(finger.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(List.of(swipe));
the duration lives on the second move. that replaces waitAction(500). note the second move is absolute (500, 600), not an offset from (500, 1600).
the Android shortcut
if you are on Android only, the whole thing can be one command:
driver.execute_script("mobile: swipeGesture", {
"left": 100, "top": 300, "width": 800, "height": 1200,
"direction": "up", "percent": 0.75
})
parameters are documented in the UiAutomator2 driver repo linked above. as of October 2026 I use this for most scrolls and swipes, and fall back to raw W3C Actions only for odd paths like drawing a pattern or a diagonal drag.
tap and long press
a tap is move, down, up. a long press is move, down, pause for the hold time, up. TouchAction’s longPress with a duration becomes an explicit pause. or use mobile: clickGesture and mobile: longClickGesture on Android and skip the sequence.
pinch and zoom (the MultiAction case)
PointerInput f1 = new PointerInput(PointerInput.Kind.TOUCH, "finger1");
PointerInput f2 = new PointerInput(PointerInput.Kind.TOUCH, "finger2");
Sequence a = new Sequence(f1, 0);
Sequence b = new Sequence(f2, 0);
// both fingers start near the centre and move apart over the same duration
a.addAction(f1.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 540, 1000));
b.addAction(f2.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 560, 1000));
a.addAction(f1.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
b.addAction(f2.createPointerDown(PointerInput.MouseButton.LEFT.asArg()));
a.addAction(f1.createPointerMove(Duration.ofMillis(400), PointerInput.Origin.viewport(), 300, 1000));
b.addAction(f2.createPointerMove(Duration.ofMillis(400), PointerInput.Origin.viewport(), 800, 1000));
a.addAction(f1.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
b.addAction(f2.createPointerUp(PointerInput.MouseButton.LEFT.asArg()));
driver.perform(List.of(a, b));
this is the direct replacement for MultiAction. the key idea: two sources, equal action counts so ticks align, one perform.
migration gotchas
- offsets became absolute: old
moveTo(x, y)was often relative to the previous point. in W3C Actions, viewport origin means absolute screen coordinates. if your swipes now fly to the wrong place, this is the first thing to check. - wait became duration: put the time on the move that should take time.
- release actions: if one test leaves a finger down, call release actions in teardown.
- element origin: you can pass an element as the origin to move relative to its centre. useful when you used element-based TouchAction taps.
- coordinates depend on device: read the window size (
get_window_size) rather than hard coding 1080 by 2400 numbers. - selectors are a separate topic: if you are also modernising locators, read is UiSelector deprecated in Android’s UI Automator.
use-case verdicts
migrating an old Appium 1.x suite to Appium 2
winner: W3C Actions. there is no real contest. centralise your gesture helpers, rewrite them once, and keep the test bodies unchanged. if you are weighing a bigger move, my comparison in Maestro vs Appium for Android covers when a different tool may be the better rewrite target.
a new Android-only suite
winner: neither, use the mobile: gesture commands, then W3C Actions for the odd path. I am calling this out because the honest answer to “which of these two” is sometimes “the third thing”.
multi-finger gestures (pinch, rotate, two-finger scroll)
winner: W3C Actions. it expresses these cleanly with one source per finger. MultiAction did too, but it is gone in current stacks.
frozen legacy environments
winner: TouchAction, by default and temporarily. if you are locked to an old server and client for a device farm or a customer reason, it still works there. write your helpers behind one function so the swap later is a single edit.
geo and carrier dependent flows
this is where the gesture API matters least. whether the screen taps register is separate from whether the app sees a Singapore mobile network. if you test on real Singapore networks, you can use real Android phones or a mobile proxy for the network side. cloudf.one rents real Android phones in Singapore on dedicated hardware, each with a persistent Singapore mobile IP, controlled from the browser. one honest limit: cloudf.one has no self-service adb. adb and Appium reach a rented phone only through a WireGuard tunnel that we set up on request (email [email protected]). it is Singapore only. only automate apps and devices you own or are authorised to test.
who should pick Vendor A
- people stuck on an Appium 1.x server and an old client for reasons outside their control.
- teams with a short-lived suite that will be retired before the next upgrade.
- nobody starting fresh.
if that is you, wrap TouchAction in a single helper module now, so the later rewrite touches one file. do not spread TouchAction(driver) through your test bodies.
who should pick Vendor B
- anyone on Appium 2 and a current client.
- anyone who needs multi-finger gestures.
- teams that share Selenium and Appium code, since the Actions model is the same standard.
- Android-only teams, who should start with the
mobile:gesture commands and add raw W3C Actions where those fall short.
also pick it if you are running tests on a rooted or non-rooted device and wondering whether gestures need root: they do not. see root vs non-root Android automation for what does change. this article is about driving your own test apps and devices through supported interfaces. I am not covering how to get around root detection, Play Integrity or any app’s anti-tamper, and I would not.
verdict overall
W3C Actions wins, and I have set the frontmatter winner to Vendor B for that reason. TouchAction was a friendly API on top of a protocol that no longer exists in current Appium. the migration is mechanical once you know the three traps: absolute coordinates, duration on the move, and releasing input state. for Android, try the mobile: gesture commands before you hand-build a sequence.
as of October 2026, my practical order of preference is: mobile: gesture commands on Android, W3C Actions for everything custom or cross-platform, and TouchAction only behind a wrapper on an environment you cannot upgrade yet. verify against your own client’s changelog, since removal dates differ by language. more automation write-ups are in the blog.
Written by Xavier Fok
disclosure: this article may contain affiliate links. if you buy through them we may earn a commission at no extra cost to you. verdicts are independent of payouts. last reviewed by Xavier Fok on 2026-10-14.