Low-Level Insights on iOS Input Events
What Are Input Events
iOS uses events concept to handle signals received from different input devices. An event is an object, which is generated in response to a signal from an input device.
These objects are then delivered to the corresponding kernel subsystem, which processes them
and notifies all listening processes about taps, key presses, swipes, etc.
This means that in order to emulate a signal generated by
an external device, such as touch screen, it is necessary to just send event objects with
the same properties and in the same sequence as they would be generated by a real device.
Lets Simulate a Single Tap
The events API itself is a part of Apple private API and is not open sourced and neither
it is documented. XCTest framework also does not expose any public APIs for input events
generation. Although, there is a possibility to perform events generation via XCTest
private undocumented APIs. In particular, we are interested in XCPointerEventPath and XCSynthesizedEventRecord interfaces.
These APIs allow to create chains of input events and supply them to the system kernel for execution.
In order to synthesize a single tap it is necessary to:
- Create a new
XCPointerEventPath
instance and init it for touch at the starting point - Add a new
liftUp
event at0.125s
offset usingliftUpAtOffset:
method - Add the generated event path object to
XCSynthesizedEventRecord
instance usingaddPointerEventPath:
method - Execute the events using
synthesizeWithError:
method ofXCSynthesizedEventRecord
instance and control the returned error
There are several limitations to these APIs:
- Each
XCPointerEventPath
instance can only be executed for a single action. If one tries to add, for example, two taps to a single path, then these are effectively ignored - Each
XCPointerEventPath
instance can only be initialized for a particular pointer type: touch, mouse (since Xcode 10.2) or keyboard (since Xcode 10.2) - Events can only be added with increasing offset values to an existing
XCPointerEventPath
instance
How About More Complicated Actions
Unfortunately, the API is private and has zero documentation.
That is why one can only figure out what it really can while playing with it and trying different
input combinations.
It is known that providing multiple XCPointerEventPath
instances with
overlapping timeouts will generate a multitouch action with the amount of fingers equal
to the amount of the supplied event paths.
So, in order to generate two-finger symmetric swipe we need to supply the following events:
- Create a two
XCPointerEventPath
instances and init them for touch at the starting point - Add a
moveToPoint
event at0.525s
offset usingmoveToPoint:
method to each path - Add a
liftUp
eventa at0.525s
offset usingliftUpAtOffset:
method to each path - Add the generated event paths to
XCSynthesizedEventRecord
instance usingaddPointerEventPath:
method - Execute the events using
synthesizeWithError:
method ofXCSynthesizedEventRecord
instance and control the returned error
Further Reading
Unfortunately, there is no information on this topic at all (private API ¯\_(ツ)_/¯
). Consider visiting the following resources:
- WebDriverAgent/PrivateHeaders/XCTest at master · appium/WebDriverAgent · GitHub
- WebDriverAgent/FBW3CTouchActionsIntegrationTests.m at master · appium/WebDriverAgent · GitHub
- WebDriverAgent/FBW3CMultiTouchActionsIntegrationTests.m at master · appium/WebDriverAgent · GitHub
- WebDriverAgent/FBW3CActionsSynthesizer.m at master · appium/WebDriverAgent · GitHub