The iPhone lacks specific methods to create UIEvent and UITouch objects. I'll show you how to add this functionality so you can write programmatically driven user-interfaces.
Update #1: Added a "Device SDK" version that will link correctly outside of the "Simulator SDK".
Update #2: Two bugs have been fixed since the code was originally posted... the objects in theUIEventmember_keyedTouchesare nowNSSets and the_viewand_windowinUITouchare now retained.
Update #3: changes to theUITouchandUIEventcategories as well asperformTouchInView:to support SDK 2.2 changes.
User-interface testing
When running application tests, it is helpful to be able to generate user events to test your user-interface. That way, you can run user-interface tests automatically instead of manually.
On Cocoa Senior (also known as "the Mac") we have methods like the gargantuan:
- mouseEventWithType:location:modifierFlags:timestamp:
- windowNumber:context:eventNumber:clickCount:pressure:
to generate events.
Cocoa Junior on the iPhone doesn't have any methods like this, so we must work out how to achieve it ourselves.
UITouch category
A basic touch event normally consists of three objects:
- The UITouch object — which will be used for the touch down and touch up
- A first UIEvent to wrap the touch down
- A second UIEvent to wrap the touch up
Lets look first at creating the UITouch object. Since most of the fields in this object are private, we can't sublcass it or set them directly — everything must be done on a category. My category goes something like this:
@implementation UITouch (Synthesize)
- (id)initInView:(UIView *)view
{
self = [super init];
if (self != nil)
{
CGRect frameInWindow;
if ([view isKindOfClass:[UIWindow class]])
{
frameInWindow = view.frame;
}
else
{
frameInWindow =
[view.window convertRect:view.frame fromView:view.superview];
}
_tapCount = 1;
_locationInWindow =
CGPointMake(
frameInWindow.origin.x + 0.5 * frameInWindow.size.width,
frameInWindow.origin.y + 0.5 * frameInWindow.size.height);
_previousLocationInWindow = _locationInWindow;
UIView *target = [view.window hitTest:_locationInWindow withEvent:nil];
_view = [target retain];
_window = [view.window retain];
_phase = UITouchPhaseBegan;
_touchFlags._firstTouchForView = 1;
_touchFlags._isTap = 1;
_timestamp = [NSDate timeIntervalSinceReferenceDate];
}
return self;
}
- (void)changeToPhase:(UITouchPhase)phase
{
_phase = phase;
_timestamp = [NSDate timeIntervalSinceReferenceDate];
}
@end
This method builds a touch in the center of the specified UIView (window coordinates must be used).
You should note that this category includes the changeToPhase: method. This phase (set to UITouchPhaseBegan in the initInView: method) refers to the begin/drag/ended state of the touch operation. We need a method to change the state because the same UITouch object must be used for touch began and touch ended events (otherwise the whole windowing system crashes).
UIEvent category
The UIEvent object has fewer fields to configure but they are tricky nonetheless:
@implementation UIEvent (Synthesize)
- (id)initWithTouch:(UITouch *)touch
{
self = [super init];
if (self != nil)
{
_touches = [[NSMutableSet setWithObject:touch] retain];
_timestamp = [NSDate timeIntervalSinceReferenceDate];
CGPoint location = [touch locationInView:touch.window];
_event = [[GSEventProxy alloc] init];
_event->x = location.x;
_event->y = location.y;
CFMutableDictionaryRef dict =
CFDictionaryCreateMutable(
kCFAllocatorDefault,
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(dict, touch.view, _touches);
CFDictionaryAddValue(dict, touch.window, _touches);
_keyedTouches = dict;
}
return self;
}
@end
The _touches and _timestamp are obvious (although the timeIntervalSinceReferenceDate value is not correct, it will do).
I'm not sure what the _keyedTouches field is used for but it always seems to take this configuration — who am I to complain.
The _event field is the most obscure. Looking at UIEvents created normally by the iPhone, this field is normally an opaque CFType called a GSEvent. This is how events are modelled internally on the iPhone. With SDK 2.2, we need to provide this object with the x and y fields in the correct place or some Apple classes (UIScrollView in particular) will fail. For this, we'll create a proxy object which has the correct data for these fields and should fulfil the same purpose. This definition will need to before the UIEvent category:
@interface GSEventProxy : NSObject
{
@public
int ignored1[5];
float x;
float y;
int ignored2[24];
}
@end
@implementation GSEventProxy
@end
Device SDK version
For the Device SDK, Apple don't allow you to link to the private instance variables of the UIEvent. You can avoid this by declaring an unrelated class and casting self in the initWithTouch: method to a variable of this class and accessing all instance variables through the recast variable.
This falls into a "gray" area about whether or not it is using an undeclared API (and therefore illegal according to the iPhone SDK Agreement). I don't recommend you try to submit an app to the App Store using this approach — it is intended for in-house app testing only.
Declare the following above the UIEvent (Synthesize) category:
@interface PublicEvent : NSObject
{
@public
CFTypeRef _event;
NSTimeInterval _timestamp;
NSMutableSet *_touches;
CFMutableDictionaryRef _keyedTouches;
}
@end
@implementation PublicEvent
@end
Then, add this following line:
PublicEvent *publicEvent = (PublicEvent *)self;
to the initWithTouch: method inside the if (self != nil) conditional and for all variables in the method starting with an underscore, access them through the publicEvent variable. i.e.:
publicEvent->_touches = [[NSSet setWithObject:touch] retain];
Sending the event
There is no API to route the events to the appropriate view — so we will just invoke the methods directly on the view ourselves.
Using the above categories to create the UITouch and UIEvent objects, dispatching a touch event to a UIView looks like this:
- (void)performTouchInView:(UIView *)view
{
UITouch *touch =
[[[UITouch alloc] initInView:view phase:UITouchPhaseBegan] autorelease];
UIEvent *eventDown =
[[[UIEvent alloc] initWithTouch:touch] autorelease];
[touch.view touchesBegan:[NSSet setWithObject:touch] withEvent:eventDown];
[touch changeToPhase:UITouchPhaseEnded];
UIEvent *eventUp =
[[[UIEvent alloc] initWithTouch:touch] autorelease];
[touch.view touchesEnded:[NSSet setWithObject:touch] withEvent:eventUp];
}
Conclusion
You can download a copy of TouchSynthesis.m as part of the SelfTesting project (from my later post Automated User Interface Testing on the iPhone).
I have only tested this for performing touch events in UITableViewCells in a UINavigationController — navigating a hierarchy to verify that the hierarchy works. Of course, once you've programmatically navigated, you must also read back from the hierarchy to ensure that required features are present — but that's a post for a different time.
Working directly with the fields of a class is always a little risky. I'm sure there are UIViews that won't work well with this type of synthetic touch. Apple is also free to change the meaning of any fields at any time. However, for unreleased user-interface test code this approach will work.

Basic HTML formatting tags (<a>, <b>, <i>, and <pre>) are permitted in comments.