From 21d86c09af54475b48cd3c12ffddd573aec087a1 Mon Sep 17 00:00:00 2001 From: Sash Zats Date: Thu, 21 Sep 2023 10:14:43 -0700 Subject: [PATCH] Preload accessibility framework if not already loaded Summary: It looks like there are some edge cases when app did not load private accessibility framework (probably if it never set any accessibility values?) This diff makes calls to accessibility hierarchy safer and ensures to preload framework if available Reviewed By: lblasa Differential Revision: D49501064 fbshipit-source-id: b46216b58bf6c9c63f900e199fea035e3262afb2 --- .../Serializers/UIDInitEvent+Foundation.m | 10 ++++-- .../Traversal/UIDAllyTraversal.h | 2 ++ .../Traversal/UIDAllyTraversal.m | 36 +++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.m index 9b6eb5065..89a2c1b27 100644 --- a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.m +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.m @@ -8,6 +8,7 @@ #if FB_SONARKIT_ENABLED #import "NSArray+Foundation.h" +#import "UIDAllyTraversal.h" #import "UIDInitEvent+Foundation.h" #import "UIDTraversalMode.h" @@ -20,12 +21,15 @@ FB_LINKABLE(UIDInitEvent_Foundation) @"frameworkEventMetadata" : self.frameworkEventMetadata ? [self.frameworkEventMetadata toFoundation] : @[], - @"supportedTraversalModes" : @[ + @"supportedTraversalModes" : UIDAllyTraversal.isSupported ? @[ NSStringFromUIDTraversalMode(UIDTraversalModeViewHierarchy), NSStringFromUIDTraversalMode(UIDTraversalModeAccessibilityHierarchy), + ] : @[ + NSStringFromUIDTraversalMode(UIDTraversalModeViewHierarchy), ], - @"currentTraversalMode" : - NSStringFromUIDTraversalMode(self.currentTraversalMode), + @"currentTraversalMode" : NSStringFromUIDTraversalMode( + UIDAllyTraversal.isSupported ? self.currentTraversalMode + : UIDTraversalModeViewHierarchy), }; } diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.h index 95c17a457..4f97a2e79 100644 --- a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.h +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.h @@ -17,6 +17,8 @@ NS_ASSUME_NONNULL_BEGIN @interface UIDAllyTraversal : NSObject +@property(nonatomic, class, readonly, getter=isSupported) BOOL supported; + - (instancetype)initWithDescriptorRegister: (UIDDescriptorRegister*)descriptorRegister; diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.m index 040a8e98b..7e0ed379d 100644 --- a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.m +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDAllyTraversal.m @@ -27,6 +27,13 @@ UIDDescriptorRegister* _descriptorRegister; } ++ (BOOL)isSupported { + return _loadAccessibilityFramework() && + [UIApplication.sharedApplication + respondsToSelector:@selector + (_accessibilityLeafDescendantsWithOptions:)]; +} + - (instancetype)initWithDescriptorRegister: (UIDDescriptorRegister*)descriptorRegister { self = [super init]; @@ -41,10 +48,18 @@ return @[]; } + if (!_loadAccessibilityFramework()) { + return @[]; + } + // create voice over representation of the app id options = [NSClassFromString(@"UIAccessibilityElementTraversalOptions") voiceOverOptionsIncludingElementsFromOpaqueProviders:YES honorsGroups:NO]; + if (![application respondsToSelector:@selector + (_accessibilityLeafDescendantsWithOptions:)]) { + return @[]; + } NSArray* const allyNodes = [[application _accessibilityLeafDescendantsWithOptions:options] mutableCopy]; @@ -78,6 +93,27 @@ return uidNode; } +static BOOL _loadAccessibilityFramework(void) { + static BOOL isAccessibilityFrameworkLoaded; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSURL* const knownFrameworkUrl = + [NSBundle bundleForClass:UIApplication.class].bundleURL; + if (!knownFrameworkUrl) { + isAccessibilityFrameworkLoaded = NO; + } else { + NSURL* const accessibilityFrameworkUrl = + [knownFrameworkUrl.URLByDeletingLastPathComponent + .URLByDeletingLastPathComponent + URLByAppendingPathComponent: + @"PrivateFrameworks/UIAccessibility.framework"]; + isAccessibilityFrameworkLoaded = + [[NSBundle bundleWithURL:accessibilityFrameworkUrl] load]; + } + }); + return isAccessibilityFrameworkLoaded; +} + static NSString* _nameForNode(NSObject* node) { NSMutableArray* const parts = [NSMutableArray new]; if (node.accessibilityLabel.length > 0) {