From 05a852ba60276759b07e66fab7674b73255e1139 Mon Sep 17 00:00:00 2001 From: Joe Vilches Date: Wed, 16 Jul 2025 09:22:21 -0700 Subject: [PATCH] Use setter for accessibilityElements Summary: There have been some hard to find crashes with iOS outlined in https://fb.workplace.com/groups/3615245781855602/permalink/23898365966450285/. Talking with lenaic this may be due to us not using the setter to set this property. Let's try that Changelog: [Internal] Reviewed By: lenaic Differential Revision: D78413954 --- .../Text/RCTParagraphComponentView.mm | 5 -- .../View/RCTViewAccessibilityElement.h | 28 ------- .../View/RCTViewAccessibilityElement.mm | 83 ------------------- .../View/RCTViewComponentView.mm | 57 +++++-------- 4 files changed, 22 insertions(+), 151 deletions(-) delete mode 100644 packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewAccessibilityElement.h delete mode 100644 packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewAccessibilityElement.mm diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm index 9fb3400d90cd..79ee7ffe434b 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm @@ -9,7 +9,6 @@ #import "RCTParagraphComponentAccessibilityProvider.h" #import -#import #import #import #import @@ -227,10 +226,6 @@ - (BOOL)isAccessibilityCoopted for (NSObject *element in elements) { if ([element isKindOfClass:[UIView class]] && [cooptingCandidates containsObject:((UIView *)element)]) { return YES; - } else if ( - [element isKindOfClass:[RCTViewAccessibilityElement class]] && - [cooptingCandidates containsObject:((RCTViewAccessibilityElement *)element).view]) { - return YES; } } } diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewAccessibilityElement.h b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewAccessibilityElement.h deleted file mode 100644 index cb7566e1b328..000000000000 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewAccessibilityElement.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTViewComponentView.h" - -#import - -NS_ASSUME_NONNULL_BEGIN - -/* - * A UIAcccessibilityElement representing a RCTViewComponentView from an - * accessibility standpoint. This enables RCTViewComponentView's to reference - * themselves in `accessibilityElements` without actually being an accessibility - * element. If it were, then iOS would not call into `accessibilityElements`. - */ -@interface RCTViewAccessibilityElement : UIAccessibilityElement - -@property (readonly) RCTViewComponentView *view; - -- (instancetype)initWithView:(RCTViewComponentView *)view; - -@end - -NS_ASSUME_NONNULL_END diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewAccessibilityElement.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewAccessibilityElement.mm deleted file mode 100644 index 5af6a99c9323..000000000000 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewAccessibilityElement.mm +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -#import "RCTViewAccessibilityElement.h" - -@implementation RCTViewAccessibilityElement - -- (instancetype)initWithView:(RCTViewComponentView *)view -{ - if (self = [super initWithAccessibilityContainer:view]) { - _view = view; - } - - return self; -} - -- (CGRect)accessibilityFrame -{ - return UIAccessibilityConvertFrameToScreenCoordinates(_view.bounds, _view); -} - -#pragma mark - Forwarding to _view - -- (NSString *)accessibilityLabel -{ - return _view.accessibilityLabel; -} - -- (NSString *)accessibilityValue -{ - return _view.accessibilityValue; -} - -- (UIAccessibilityTraits)accessibilityTraits -{ - return _view.accessibilityTraits; -} - -- (NSString *)accessibilityHint -{ - return _view.accessibilityHint; -} - -- (BOOL)accessibilityIgnoresInvertColors -{ - return _view.accessibilityIgnoresInvertColors; -} - -- (BOOL)shouldGroupAccessibilityChildren -{ - return _view.shouldGroupAccessibilityChildren; -} - -- (NSArray *)accessibilityCustomActions -{ - return _view.accessibilityCustomActions; -} - -- (NSString *)accessibilityLanguage -{ - return _view.accessibilityLanguage; -} - -- (BOOL)accessibilityViewIsModal -{ - return _view.accessibilityViewIsModal; -} - -- (BOOL)accessibilityElementsHidden -{ - return _view.accessibilityElementsHidden; -} - -- (BOOL)accessibilityRespondsToUserInteraction -{ - return _view.accessibilityRespondsToUserInteraction; -} - -@end diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm index a99f103a33e6..ad9c47d161e9 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/View/RCTViewComponentView.mm @@ -6,7 +6,6 @@ */ #import "RCTViewComponentView.h" -#import "RCTViewAccessibilityElement.h" #import #import @@ -51,8 +50,6 @@ @implementation RCTViewComponentView { UIView *_containerView; BOOL _useCustomContainerView; NSMutableSet *_accessibilityOrderNativeIDs; - NSMutableArray *_accessibilityElements; - RCTViewAccessibilityElement *_axElementDescribingSelf; } #ifdef RCT_DYNAMIC_FRAMEWORKS @@ -405,7 +402,11 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & [_accessibilityOrderNativeIDs addObject:RCTNSStringFromString(childId)]; } - _accessibilityElements = [NSMutableArray new]; + // If we are prop updating and have children we can go ahead and assign this prop. + // Otherwise, we might not have children attached yet and need to wait before then. + if (self.currentContainerView.subviews.count > 0) { + [self updateAccessibilityElements]; + } } // `accessibilityTraits` @@ -617,7 +618,6 @@ - (void)prepareForRecycle _isJSResponder = NO; _removeClippedSubviews = NO; _reactSubviews = [NSMutableArray new]; - _accessibilityElements = [NSMutableArray new]; } - (void)setPropKeysManagedByAnimated_DO_NOT_USE_THIS_IS_BROKEN:(NSSet *_Nullable)props @@ -1149,43 +1149,37 @@ - (NSObject *)accessibilityElement return self; } -- (NSArray *)accessibilityElements +- (void)didMoveToSuperview { - if ([_accessibilityOrderNativeIDs count] <= 0) { - return super.accessibilityElements; + // At this point we are guaranteed to have subviews, if we are going to have them + if (ReactNativeFeatureFlags::enableAccessibilityOrder()) { + [self updateAccessibilityElements]; } +} - // TODO: Currently this ignores changes to descendant nativeID's. While that should rarely, if ever happen, it's an - // edge case we should address. Currently this fixes some app deaths so landing this without addressing that edge case - // for now. - if ([_accessibilityElements count] > 0) { - return _accessibilityElements; +- (void)updateAccessibilityElements +{ + if ([_accessibilityOrderNativeIDs count] == 0) { + self.accessibilityElements = nil; + return; } NSMutableDictionary *nativeIdToView = [NSMutableDictionary new]; - [RCTViewComponentView collectAccessibilityElements:self intoDictionary:nativeIdToView nativeIds:_accessibilityOrderNativeIDs]; - for (auto childId : _props->accessibilityOrder) { + NSMutableArray *accessibilityElements = [NSMutableArray new]; + for (const auto &childId : _props->accessibilityOrder) { NSString *nsStringChildId = RCTNSStringFromString(childId); - // Special case to allow for self-referencing with accessibilityOrder - if ([nsStringChildId isEqualToString:self.nativeId]) { - if (!_axElementDescribingSelf) { - _axElementDescribingSelf = [[RCTViewAccessibilityElement alloc] initWithView:self]; - } - _axElementDescribingSelf.isAccessibilityElement = [super isAccessibilityElement]; - [_accessibilityElements addObject:_axElementDescribingSelf]; - } else { - UIView *viewWithMatchingNativeId = [nativeIdToView objectForKey:nsStringChildId]; - if (viewWithMatchingNativeId) { - [_accessibilityElements addObject:viewWithMatchingNativeId]; - } + + UIView *viewWithMatchingNativeId = [nativeIdToView objectForKey:nsStringChildId]; + if (viewWithMatchingNativeId != nil) { + [accessibilityElements addObject:viewWithMatchingNativeId]; } } - return _accessibilityElements; + self.accessibilityElements = accessibilityElements; } + (void)collectAccessibilityElements:(UIView *)view @@ -1252,13 +1246,6 @@ - (BOOL)isAccessibilityElement return self.contentView.isAccessibilityElement; } - // If we reference ourselves in accessibilityOrder then we will make a - // UIAccessibilityElement object to represent ourselves since returning YES - // here would mean iOS would not call into accessibilityElements - if ([_accessibilityOrderNativeIDs containsObject:self.nativeId]) { - return NO; - } - return [super isAccessibilityElement]; }