Skip to content

Commit cd857b6

Browse files
authored
Merge branch 'master' into feat/skill-for-running-RNN-on-Engine-e2e-tests-
2 parents 7764cab + d16f85b commit cd857b6

8 files changed

Lines changed: 191 additions & 18 deletions

ios/RNNBottomTabsController.mm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#import "RNNBottomTabsController.h"
2+
#import "UITabBarController+RNNOptions.h"
23
#import "UITabBarController+RNNUtils.h"
34

45
@interface RNNBottomTabsController ()
@@ -100,6 +101,8 @@ - (void)createTabBarItems:(NSArray<UIViewController *> *)childViewControllers {
100101
for (UIViewController *child in childViewControllers) {
101102
[_bottomTabPresenter applyOptions:child.resolveOptions child:child];
102103
}
104+
105+
[self syncTabBarItemTestIDs];
103106
}
104107

105108
- (void)mergeChildOptions:(RNNNavigationOptions *)options child:(UIViewController *)child {
@@ -111,6 +114,8 @@ - (void)mergeChildOptions:(RNNNavigationOptions *)options child:(UIViewControlle
111114
[_dotIndicatorPresenter mergeOptions:options
112115
resolvedOptions:childViewController.resolveOptions
113116
child:childViewController];
117+
118+
[self syncTabBarItemTestIDs];
114119
}
115120

116121
- (id<UITabBarControllerDelegate>)delegate {
@@ -123,6 +128,7 @@ - (void)render {
123128

124129
- (void)viewDidLayoutSubviews {
125130
[super viewDidLayoutSubviews];
131+
[self syncTabBarItemTestIDs];
126132
[self.presenter viewDidLayoutSubviews];
127133
[_dotIndicatorPresenter bottomTabsDidLayoutSubviews:self];
128134
}

ios/UINavigationController+RNNOptions.mm

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,52 @@ - (void)setNavigationBarClipsToBounds:(BOOL)clipsToBounds {
6262
self.navigationBar.clipsToBounds = clipsToBounds;
6363
}
6464

65-
- (void)setBackButtonTestID:(NSString *)testID {
66-
UIView *navigationBarContentView =
65+
- (UIView *)rnn_findBackButtonView {
66+
UIView *contentView =
6767
[self.navigationBar findChildByClass:NSClassFromString(@"_UINavigationBarContentView")];
68-
UIView *barButton =
69-
[navigationBarContentView findChildByClass:NSClassFromString(@"_UIButtonBarButton")];
70-
if (barButton)
71-
barButton.accessibilityIdentifier = testID;
68+
if (!contentView) {
69+
contentView =
70+
[self.navigationBar findChildByClass:NSClassFromString(@"UIKit.NavigationBarContentView")];
71+
}
72+
if (!contentView)
73+
return nil;
74+
75+
Class buttonClass = NSClassFromString(@"_UIButtonBarButton");
76+
77+
return [contentView findDescendantByClass:buttonClass
78+
passingTest:^BOOL(UIView *view) {
79+
if ([view respondsToSelector:NSSelectorFromString(@"isBackButton")]) {
80+
return [[view valueForKey:@"backButton"] boolValue];
81+
}
82+
return YES;
83+
}];
84+
}
85+
86+
- (void)rnn_applyTestID:(NSString *)testID toBackButtonView:(UIView *)barButton {
87+
barButton.accessibilityIdentifier = testID;
88+
barButton.isAccessibilityElement = YES;
89+
}
90+
91+
- (void)setBackButtonTestID:(NSString *)testID {
92+
if (!testID)
93+
return;
94+
95+
UIView *barButton = [self rnn_findBackButtonView];
96+
if (barButton) {
97+
[self rnn_applyTestID:testID toBackButtonView:barButton];
98+
} else {
99+
__weak UINavigationController *weakSelf = self;
100+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.15 * NSEC_PER_SEC)),
101+
dispatch_get_main_queue(), ^{
102+
UINavigationController *nc = weakSelf;
103+
if (!nc)
104+
return;
105+
UIView *btn = [nc rnn_findBackButtonView];
106+
if (btn) {
107+
[nc rnn_applyTestID:testID toBackButtonView:btn];
108+
}
109+
});
110+
}
72111
}
73112

74113
- (CGFloat)getTopBarHeight {

ios/UITabBarController+RNNOptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@
2020

2121
- (void)hideTabBar:(BOOL)animated;
2222

23+
- (void)syncTabBarItemTestIDs;
24+
2325
@end

ios/UITabBarController+RNNOptions.mm

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,85 @@
11
#import "RNNBottomTabsController.h"
22
#import "UITabBar+utils.h"
33
#import "UITabBarController+RNNOptions.h"
4+
#import <objc/runtime.h>
5+
6+
static const NSTimeInterval RNNTabBarTestIDRetryDelay = 0.15;
7+
static const NSUInteger RNNTabBarTestIDMaxRetryAttempts = 5;
8+
static const void *RNNTabBarTestIDRetryScheduledKey = &RNNTabBarTestIDRetryScheduledKey;
9+
static const void *RNNTabBarTestIDRetryAttemptsKey = &RNNTabBarTestIDRetryAttemptsKey;
10+
static const void *RNNOriginalTabBarViewAccessibilityIdentifierKey =
11+
&RNNOriginalTabBarViewAccessibilityIdentifierKey;
412

513
@implementation UITabBarController (RNNOptions)
614

15+
- (void)rnn_storeOriginalAccessibilityIdentifierIfNeededForTabView:(UIView *)tabView {
16+
if (objc_getAssociatedObject(tabView, RNNOriginalTabBarViewAccessibilityIdentifierKey))
17+
return;
18+
19+
id originalAccessibilityIdentifier = tabView.accessibilityIdentifier ?: [NSNull null];
20+
objc_setAssociatedObject(tabView, RNNOriginalTabBarViewAccessibilityIdentifierKey,
21+
originalAccessibilityIdentifier,
22+
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
23+
}
24+
25+
- (void)rnn_applyTestID:(NSString *)testID toTabView:(UIView *)tabView {
26+
[self rnn_storeOriginalAccessibilityIdentifierIfNeededForTabView:tabView];
27+
tabView.accessibilityIdentifier = testID;
28+
}
29+
30+
- (void)rnn_restoreOriginalAccessibilityIdentifierForTabView:(UIView *)tabView {
31+
id originalAccessibilityIdentifier =
32+
objc_getAssociatedObject(tabView, RNNOriginalTabBarViewAccessibilityIdentifierKey);
33+
if (!originalAccessibilityIdentifier)
34+
return;
35+
36+
tabView.accessibilityIdentifier =
37+
[originalAccessibilityIdentifier isKindOfClass:NSNull.class]
38+
? nil
39+
: originalAccessibilityIdentifier;
40+
objc_setAssociatedObject(tabView, RNNOriginalTabBarViewAccessibilityIdentifierKey, nil,
41+
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
42+
}
43+
44+
- (BOOL)rnn_isTabBarTestIDRetryScheduled {
45+
return [objc_getAssociatedObject(self, RNNTabBarTestIDRetryScheduledKey) boolValue];
46+
}
47+
48+
- (void)rnn_setTabBarTestIDRetryScheduled:(BOOL)scheduled {
49+
objc_setAssociatedObject(self, RNNTabBarTestIDRetryScheduledKey, @(scheduled),
50+
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
51+
}
52+
53+
- (NSUInteger)rnn_tabBarTestIDRetryAttempts {
54+
NSNumber *retryAttempts = objc_getAssociatedObject(self, RNNTabBarTestIDRetryAttemptsKey);
55+
return retryAttempts.unsignedIntegerValue;
56+
}
57+
58+
- (void)rnn_setTabBarTestIDRetryAttempts:(NSUInteger)retryAttempts {
59+
objc_setAssociatedObject(self, RNNTabBarTestIDRetryAttemptsKey, @(retryAttempts),
60+
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
61+
}
62+
63+
- (BOOL)rnn_applyTabBarItemTestIDs {
64+
NSArray<UITabBarItem *> *items = self.tabBar.items ?: @[];
65+
BOOL appliedAllKnownTestIDs = YES;
66+
67+
for (NSUInteger tabIndex = 0; tabIndex < items.count; tabIndex++) {
68+
UITabBarItem *item = items[tabIndex];
69+
NSString *testID = item.accessibilityIdentifier;
70+
UIView *tabView = [self.tabBar tabBarItemViewAtIndex:tabIndex];
71+
if (testID.length > 0 && tabView) {
72+
[self rnn_applyTestID:testID toTabView:tabView];
73+
} else if (tabView) {
74+
[self rnn_restoreOriginalAccessibilityIdentifierForTabView:tabView];
75+
} else if (testID.length > 0) {
76+
appliedAllKnownTestIDs = NO;
77+
}
78+
}
79+
80+
return appliedAllKnownTestIDs;
81+
}
82+
783
- (void)setCurrentTabIndex:(NSUInteger)currentTabIndex {
884
[self setSelectedIndex:currentTabIndex];
985
}
@@ -16,6 +92,38 @@ - (void)setTabBarTestID:(NSString *)testID {
1692
self.tabBar.accessibilityIdentifier = testID;
1793
}
1894

95+
- (void)syncTabBarItemTestIDs {
96+
if ([self rnn_applyTabBarItemTestIDs]) {
97+
[self rnn_setTabBarTestIDRetryScheduled:NO];
98+
[self rnn_setTabBarTestIDRetryAttempts:0];
99+
return;
100+
}
101+
102+
if ([self rnn_isTabBarTestIDRetryScheduled])
103+
return;
104+
105+
NSUInteger retryAttempts = [self rnn_tabBarTestIDRetryAttempts];
106+
if (retryAttempts >= RNNTabBarTestIDMaxRetryAttempts) {
107+
[self rnn_setTabBarTestIDRetryAttempts:0];
108+
return;
109+
}
110+
111+
[self rnn_setTabBarTestIDRetryScheduled:YES];
112+
[self rnn_setTabBarTestIDRetryAttempts:retryAttempts + 1];
113+
114+
__weak UITabBarController *weakSelf = self;
115+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
116+
(int64_t)(RNNTabBarTestIDRetryDelay * NSEC_PER_SEC)),
117+
dispatch_get_main_queue(), ^{
118+
UITabBarController *controller = weakSelf;
119+
if (!controller)
120+
return;
121+
122+
[controller rnn_setTabBarTestIDRetryScheduled:NO];
123+
[controller syncTabBarItemTestIDs];
124+
});
125+
}
126+
19127
- (void)setTabBarStyle:(UIBarStyle)barStyle {
20128
self.tabBar.barStyle = barStyle;
21129
}

ios/Utils/UIView+Utils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ typedef NS_ENUM(NSInteger, ViewType) {
1212

1313
- (UIView *)findChildByClass:clazz;
1414

15+
- (UIView *)findDescendantByClass:clazz;
16+
17+
- (UIView *)findDescendantByClass:(Class)clazz passingTest:(BOOL (^)(UIView *view))test;
18+
1519
- (ViewType)viewType;
1620

1721
- (void)stopMomentumScrollViews;

ios/Utils/UIView+Utils.mm

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,28 @@ - (UIView *)findChildByClass:(id)clazz {
1818
return nil;
1919
}
2020

21+
- (UIView *)findDescendantByClass:(id)clazz {
22+
for (UIView *child in [self subviews]) {
23+
if ([child isKindOfClass:clazz])
24+
return child;
25+
UIView *found = [child findDescendantByClass:clazz];
26+
if (found)
27+
return found;
28+
}
29+
return nil;
30+
}
31+
32+
- (UIView *)findDescendantByClass:(Class)clazz passingTest:(BOOL (^)(UIView *view))test {
33+
for (UIView *child in [self subviews]) {
34+
if ([child isKindOfClass:clazz] && test(child))
35+
return child;
36+
UIView *found = [child findDescendantByClass:clazz passingTest:test];
37+
if (found)
38+
return found;
39+
}
40+
return nil;
41+
}
42+
2143
- (ViewType)viewType {
2244
#ifdef RCT_NEW_ARCH_ENABLED
2345
if ([self isKindOfClass:[RCTImageComponentView class]]) {

package.json

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@
123123
"eslint-plugin-jest": "^28.11.0",
124124
"eslint-plugin-prettier": "3.1.4",
125125
"github-release-notes": "https://github.com/yogevbd/github-release-notes/tarball/e601b3dba72dcd6cba323c1286ea6dd0c0110b58",
126-
"husky": "4.2.5",
126+
127127
"identity-obj-proxy": "3.0.0",
128128
"jest": "^29.6.3",
129129
"lint-staged": "10.2.11",
@@ -152,11 +152,7 @@
152152
"playground"
153153
],
154154
"packageManager": "yarn@4.12.0",
155-
"husky": {
156-
"hooks": {
157-
"pre-commit": "lint-staged"
158-
}
159-
},
155+
160156
"lint-staged": {
161157
"*.{js,ts,tsx}": "eslint --fix",
162158
"*.{h,m,mm}": "node ./scripts/check-clang-format",

playground/package.json

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"eslint-plugin-jest": "^28.11.0",
6262
"eslint-plugin-prettier": "3.1.4",
6363
"github-release-notes": "https://github.com/yogevbd/github-release-notes/tarball/e601b3dba72dcd6cba323c1286ea6dd0c0110b58",
64-
"husky": "4.2.5",
64+
6565
"identity-obj-proxy": "3.0.0",
6666
"jest": "^29.6.3",
6767
"lint-staged": "10.2.11",
@@ -85,11 +85,7 @@
8585
"typedoc": "0.x.x",
8686
"typescript": "^5.8.3"
8787
},
88-
"husky": {
89-
"hooks": {
90-
"pre-commit": "lint-staged"
91-
}
92-
},
88+
9389
"lint-staged": {
9490
"*.{js,ts,tsx}": "eslint --fix",
9591
"*.{h,m,mm}": "node ./scripts/check-clang-format"

0 commit comments

Comments
 (0)