Last active
December 5, 2018 19:47
-
-
Save steipete/08974d7544d328fb7978c10505834e00 to your computer and use it in GitHub Desktop.
If you get an [NSProxy doesNotRecognizeSelector:_accessibilityLoadAccessibilityInformation] crash in iOS 12, here's a temporary fix for your tests. Please change the prefix before you use this! MIT licensed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static void PSPDFFixiOS12AccessibilityTestCrash(void) { | |
let accessibilityLoaderClass = NSClassFromString(@"UIAccessibilityInformationLoader"); | |
let accessibilitySEL = NSSelectorFromString(@"_loadAccessibilityInformationOnMainThread:"); | |
__block IMP originalIMP = pspdf_swizzleSelectorWithBlock(accessibilityLoaderClass, accessibilitySEL, ^(id _self, BOOL onMainThread) { | |
@try { | |
((void (*)(id, SEL, BOOL))originalIMP)(_self, accessibilitySEL, onMainThread); | |
} @catch (NSException *exception) { | |
NSLog(@"Exception received: %@", exception); | |
if ([exception.name isEqualToString:NSInvalidArgumentException] && [exception.reason containsString:@"_accessibilityLoadAccessibilityInformation"]) { | |
NSLog(@"Ignoring IOS 12b5 weirdness..."); | |
} else { | |
// Rethrow other things | |
@throw exception; | |
} | |
} | |
}); | |
} | |
// If you don't yet have such swizzling helpers, here's ours: | |
// Extracted straight from the ObjC Runtime headers: | |
// https://opensource.apple.com/source/objc4/objc4-493.9/runtime/objc-abi.h | |
// | |
// objc_msgSendSuper2() takes the current search class, not its superclass. | |
OBJC_EXPORT id objc_msgSendSuper2(struct objc_super *super, SEL op, ...) | |
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); | |
OBJC_EXPORT void objc_msgSendSuper2_stret(struct objc_super *super, SEL op,...) | |
__OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0); | |
// http://defagos.github.io/yet_another_article_about_method_swizzling/ | |
// Returns the original implementation | |
PSPDF_EXTERN _Nullable IMP pspdf_swizzleSelector(Class clazz, SEL selector, IMP newImplementation) { | |
NSCParameterAssert(clazz); | |
NSCParameterAssert(selector); | |
NSCParameterAssert(newImplementation); | |
// If the method does not exist for this class, do nothing. | |
const Method method = class_getInstanceMethod(clazz, selector); | |
if (!method) { | |
PSPDFLogError(@"%@ doesn't exist in %@.", NSStringFromSelector(selector), NSStringFromClass(clazz)); | |
// Cannot swizzle methods which are not implemented by the class or one of its parents. | |
return NULL; | |
} | |
// Make sure the class implements the method. If this is not the case, inject an implementation, only calling 'super'. | |
const char *types = method_getTypeEncoding(method); | |
@synchronized(clazz) { | |
// class_addMethod will simply return NO if the method is already implemented. | |
#if !defined(__arm64__) | |
// Sufficiently large struct | |
typedef struct LargeStruct_ { char dummy[16]; } LargeStruct; | |
NSUInteger retSize = 0; | |
NSGetSizeAndAlignment(types, &retSize, NULL); | |
// Large structs on 32-bit architectures | |
// TODO: This is incorrect for some structs on some architectures. Needs to be hardcoded, this cannot be safely inferred at runtime. | |
// https://twitter.com/gparker/status/1028564412339113984 | |
if (sizeof(void *) == 4 && types[0] == _C_STRUCT_B && retSize != 1 && retSize != 2 && retSize != 4 && retSize != 8) { | |
class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) { | |
struct objc_super super = {self, clazz}; | |
return ((LargeStruct(*)(struct objc_super *, SEL, va_list))objc_msgSendSuper2_stret)(&super, selector, argp); | |
}), types); | |
} | |
// All other cases | |
else { | |
#endif | |
class_addMethod(clazz, selector, imp_implementationWithBlock(^(__unsafe_unretained id self, va_list argp) { | |
struct objc_super super = {self, clazz}; | |
return ((id(*)(struct objc_super *, SEL, va_list))objc_msgSendSuper2)(&super, selector, argp); | |
}), types); | |
#if !defined(__arm64__) | |
} | |
#endif | |
// Swizzling | |
return class_replaceMethod(clazz, selector, newImplementation, types); | |
} | |
} | |
PSPDF_EXTERN _Nullable IMP pspdf_swizzleSelectorWithBlock(Class clazz, SEL selector, id newImplementationBlock) { | |
const IMP newImplementation = imp_implementationWithBlock(newImplementationBlock); | |
return pspdf_swizzleSelector(clazz, selector, newImplementation); | |
} |
@steipete I'm getting this error in a production app using PSPDFKit 7.6.2. Was this fixed on a later release? Thanks.
This has been fixed in PSPDFKit 7.7.2 for iOS.
Here's the detailed changelog: https://pspdfkit.com/changelog/ios/#7.7.2
See: Fixes an issue where iOS 12 could throw an exception inside -[UIAccessibilityInformationLoader _loadAccessibilityInformationOnMainThread:]. (#16489)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Yes, we fixed the bug by fixing our proxy forwarder logic. The 3-step process from respondsToSelector, methodSignature and forwardInvocation needs to be consistent for any call - even unexpected ones. We use a pre-built cache to speed up lookups and didn't consider the case where unexpected calls might be sent - especially for custom Apple-internal object categories that might skip the respondsToSelector part.