I'm making a Swift translation of a helper utility to turn on accessibility in the iOS simulator (code copied below), but I'm missing something about the c interop details because it generates a General Protection Fault.
Original code:
@interface ASAccessibilityEnabler : NSObject
@end
@implementation ASAccessibilityEnabler
+ (void)load;
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
void *handle = [self loadDylib:@"/usr/lib/libAccessibility.dylib"];
if (!handle) {
[NSException raise:NSGenericException format:@"Could not enable accessibility"];
}
int (*_AXSAutomationEnabled)(void) = dlsym(handle, "_AXSAutomationEnabled");
void (*_AXSSetAutomationEnabled)(int) = dlsym(handle, "_AXSSetAutomationEnabled");
int initialValue = _AXSAutomationEnabled();
_AXSSetAutomationEnabled(YES);
atexit_b(^{
_AXSSetAutomationEnabled(initialValue);
});
});
}
+ (void *)loadDylib:(NSString *_Nonnull)path;
{
NSDictionary *environment = [[NSProcessInfo processInfo] environment];
NSString *simulatorRoot = [environment objectForKey:@"IPHONE_SIMULATOR_ROOT"];
if (simulatorRoot) {
path = [simulatorRoot stringByAppendingPathComponent:path];
}
return dlopen([path fileSystemRepresentation], RTLD_LOCAL);
}
@end
My Swift translation (with the GPF marked as a comment):
var accessibilityEnabled = false
func enableAccessibility() {
guard !accessibilityEnabled else { return }
guard
let handle = loadDylibInSimulator(name: "/usr/lib/libAccessibility.dylib"),
let _AXSAutomationEnabled = dlsym(handle, "_AXSAutomationEnabled")?
.bindMemory(to: (@convention(c) () -> CInt).self, capacity: 1),
let _AXSSetAutomationEnabled = dlsym(handle, "_AXSSetAutomationEnabled")?
.bindMemory(to: (@convention(c) (CInt) -> Void).self, capacity: 1)
else { fatalError("sad trombone") }
accessibilityEnabled = true
let initialValue = _AXSAutomationEnabled.pointee() // <--- Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
_AXSSetAutomationEnabled.pointee(1)
atexit_b {
_AXSSetAutomationEnabled.pointee(initialValue)
}
}
func loadDylibInSimulator(name: String) -> UnsafeMutableRawPointer? {
let environment = ProcessInfo.processInfo.environment
guard let simulatorRoot = environment["IPHONE_SIMULATOR_ROOT"] else { return nil }
let url = URL(fileURLWithPath: simulatorRoot).appendingPathComponent(name)
let cString = FileManager.default.fileSystemRepresentation(withPath: url.path)
return dlopen(cString, RTLD_LOCAL)
}
I assume it's a mistake in my translation from Objective C to Swift: what is the correct translation of those function pointers into Swift?