Я провел вчера, пытаясь заставить это работать в конечном итоге в результате своего рода решения. Я не совсем доволен этим, но это было лучшее, что я мог сделать на данный момент - если кто-нибудь может предложить какие-либо улучшения или рабочую альтернативу, я бы приветствовал их ...
Во всяком случае, для всех, кто пытается сделать что-то подобное. Я основал свое решение на API, подробно описанном в этого поста - я записал последовательность событий, которую хотел смоделировать, затем воспроизвел их. Единственная загвоздка в том, что я не смог заставить работать встроенный API воспроизведения (я получил тот же сбой, который упоминался в комментариях внизу). Спустя некоторое время, копаясь в земле ASM, я закончил тем, что написал свою собственную версию.
@implementation UIApplication (EventReplay)
///
/// - replayEventsFromFile:
///
- (void)replayEventsFromFile:(NSString *)filename
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:filename];
NSArray* eventList = [[NSArray arrayWithContentsOfFile:filePath] retain];
[self replayEvents:eventList];
}
///
/// - replayEvents:
///
- (void)replayEvents:(NSArray *)events
{
if (!events.count)
return;
NSDictionary *eventDict = [events objectAtIndex:0U];
GSEventRef thisEvent = GSEventCreateWithPlist((CFDictionaryRef)eventDict);
uint64_t eventTime = thisEvent->record.timestamp;
thisEvent->record.timestamp = mach_absolute_time();
mach_port_t appPort = GSCopyPurpleNamedPort([[[NSBundle mainBundle] bundleIdentifier] UTF8String]);
GSSendEvent(&thisEvent->record, appPort);
mach_port_deallocate(mach_task_self(), appPort);
if (events.count <= 1)
return;
NSIndexSet *remainderIndexes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, events.count - 1)];
NSArray *remainingEvents = [events objectsAtIndexes:remainderIndexes];
GSEventRef nextEvent = GSEventCreateWithPlist((CFDictionaryRef)[remainingEvents objectAtIndex:0U]);
NSTimeInterval nextEventDelay = GetTimeDelta(nextEvent->record.timestamp, eventTime);
if (nextEventDelay > 0.05)
[self performSelector:@selector(replayEvents:) withObject:remainingEvents afterDelay:nextEventDelay];
else
[self replayEvents:remainingEvents];
CFRelease(nextEvent);
CFRelease(thisEvent);
}
@end
Фрагмент выше показывает, как я воспроизводю события. Моя реализация довольно грубая - вы поймете, что мне пришлось обдумать тот факт, что если я слепо использую таймер для планирования следующего события, иногда оно не срабатывает - похоже, когда задержка слишком мала. Кажется, что ужасный взлом заставляет все работать нормально
В любом случае, надеюсь, это поможет кому-то еще.