appium-mac2-driver 4.0.7 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [4.1.1](https://github.com/appium/appium-mac2-driver/compare/v4.1.0...v4.1.1) (2026-08-08)
2
+
3
+ ### Bug Fixes
4
+
5
+ * support WebDriverAgent builds with Xcode 27 ([#411](https://github.com/appium/appium-mac2-driver/issues/411)) ([8cd23fc](https://github.com/appium/appium-mac2-driver/commit/8cd23fc7485b0c6f0a0711374df627376ad4657e))
6
+
7
+ ## [4.1.0](https://github.com/appium/appium-mac2-driver/compare/v4.0.7...v4.1.0) (2026-08-07)
8
+
9
+ ### Features
10
+
11
+ * locate WKWebView web elements by DOM id via AXDOMIdentifier (opt-in) ([#405](https://github.com/appium/appium-mac2-driver/issues/405)) ([9f7cf35](https://github.com/appium/appium-mac2-driver/commit/9f7cf3570fef0c8a449dcef038563427807e8cd7))
12
+
1
13
  ## [4.0.7](https://github.com/appium/appium-mac2-driver/compare/v4.0.6...v4.0.7) (2026-08-06)
2
14
 
3
15
  ### Miscellaneous Chores
@@ -17,6 +17,7 @@
17
17
  #import <XCTest/XCTest.h>
18
18
 
19
19
  #import "AMIntegrationTestCase.h"
20
+ #import "FBConfiguration.h"
20
21
  #import "FBTestMacros.h"
21
22
  #import "XCUIElement+AMAttributes.h"
22
23
  #import "XCUIElement+AMEditable.h"
@@ -76,6 +77,19 @@
76
77
  XCTAssertEqual(type, XCUIElementTypeButton);
77
78
  }
78
79
 
80
+ - (void)testIdentifierAttributeWhileDomIdFallbackIsEnabled
81
+ {
82
+ XCUIElement *closeButton = [self.testedApplication.buttons matchingIdentifier:@"_XCUI:CloseWindow"].firstMatch;
83
+ FBConfiguration.sharedConfiguration.useDomIdAsAccessibilityId = YES;
84
+ @try {
85
+ // A native identifier must never be replaced by a DOM one
86
+ NSString *identifier = [closeButton am_wdAttributeValueWithName:@"identifier"];
87
+ XCTAssertEqualObjects(identifier, @"_XCUI:CloseWindow");
88
+ } @finally {
89
+ FBConfiguration.sharedConfiguration.useDomIdAsAccessibilityId = NO;
90
+ }
91
+ }
92
+
79
93
  - (void)testDisabledAttribute
80
94
  {
81
95
  NSString *buttonTitle = @"I Am Disabled";
@@ -17,6 +17,7 @@
17
17
  #import <XCTest/XCTest.h>
18
18
 
19
19
  #import "AMIntegrationTestCase.h"
20
+ #import "FBConfiguration.h"
20
21
  #import "XCUIElement+FBFind.h"
21
22
  #import "XCUIElement+FBClassChain.h"
22
23
 
@@ -43,6 +44,25 @@
43
44
  XCTAssertEqualObjects(matches.firstObject.identifier, @"_XCUI:CloseWindow");
44
45
  }
45
46
 
47
+ - (void)testSingleDescendantWithIdentifierWhileDomIdFallbackIsEnabled
48
+ {
49
+ FBConfiguration.sharedConfiguration.useDomIdAsAccessibilityId = YES;
50
+ @try {
51
+ // The application under test hosts no web content, so the fallback must find
52
+ // nothing extra and native identifiers must keep resolving as usual
53
+ NSArray<XCUIElement *> *matches = [self.testedApplication fb_descendantsMatchingIdentifier:@"_XCUI:CloseWindow"
54
+ shouldReturnAfterFirstMatch:NO];
55
+ XCTAssertEqual(matches.count, 1);
56
+ XCTAssertEqualObjects(matches.firstObject.identifier, @"_XCUI:CloseWindow");
57
+
58
+ NSArray<XCUIElement *> *noMatches = [self.testedApplication fb_descendantsMatchingIdentifier:@"there_is_no_such_dom_id"
59
+ shouldReturnAfterFirstMatch:NO];
60
+ XCTAssertEqual(noMatches.count, 0);
61
+ } @finally {
62
+ FBConfiguration.sharedConfiguration.useDomIdAsAccessibilityId = NO;
63
+ }
64
+ }
65
+
46
66
  - (void)testSingleDescendantWithClassName
47
67
  {
48
68
  NSArray<XCUIElement *> *matches = [self.testedApplication fb_descendantsMatchingClassName:@"XCUIElementTypeButton"
@@ -50,6 +50,16 @@ NS_ASSUME_NONNULL_BEGIN
50
50
  */
51
51
  - (BOOL)am_hasKeyboardInputFocus;
52
52
 
53
+ /**
54
+ The element's identifier as WebDriver reports it: the standard accessibility
55
+ identifier, falling back to the WebKit DOM identifier for web nodes, which leave the
56
+ standard one empty. The fallback only applies while useDomIdAsAccessibilityId is
57
+ enabled, and only takes a snapshot when it is actually needed.
58
+
59
+ @return The identifier. Could be an empty string
60
+ */
61
+ - (NSString *)am_wdIdentifier;
62
+
53
63
  @end
54
64
 
55
65
  NS_ASSUME_NONNULL_END
@@ -17,6 +17,7 @@
17
17
  #import "XCUIElement+AMAttributes.h"
18
18
 
19
19
  #import "AMGeometryUtils.h"
20
+ #import "AMSnapshotUtils.h"
20
21
  #import "FBConfiguration.h"
21
22
  #import "FBElementTypeTransformer.h"
22
23
  #import "FBElementUtils.h"
@@ -50,7 +51,7 @@
50
51
  } else if ([wdAttributeName isEqualToString:FBStringify(XCUIElement, value)]) {
51
52
  return [FBElementUtils stringValueWithValue:self.value];
52
53
  } else if ([wdAttributeName isEqualToString:FBStringify(XCUIElement, identifier)]) {
53
- return self.identifier;
54
+ return self.am_wdIdentifier;
54
55
  }
55
56
  // This should not happen
56
57
  NSString *description = [NSString stringWithFormat:@"The attribute '%@' is unknown", wdAttributeName];
@@ -59,6 +60,16 @@
59
60
  userInfo:@{}];
60
61
  }
61
62
 
63
+ - (NSString *)am_wdIdentifier
64
+ {
65
+ NSString *identifier = self.identifier;
66
+ // Snapshotting is not free, so only pay for it when the fallback can apply.
67
+ if (identifier.length > 0 || !FBConfiguration.sharedConfiguration.useDomIdAsAccessibilityId) {
68
+ return identifier;
69
+ }
70
+ return [AMSnapshotUtils wdIdentifierWithSnapshot:[self snapshotWithError:nil]] ?: identifier;
71
+ }
72
+
62
73
  - (NSDictionary<NSString *, NSNumber *> *)am_rect
63
74
  {
64
75
  return AMCGRectToDict(self.frame);
@@ -9,12 +9,50 @@
9
9
 
10
10
  #import "XCUIElement+FBFind.h"
11
11
 
12
+ #import "AMSnapshotUtils.h"
13
+
14
+ #import "FBConfiguration.h"
12
15
  #import "FBElementTypeTransformer.h"
13
16
  #import "FBElementUtils.h"
14
17
  #import "FBXPath.h"
15
18
  #import "NSPredicate+FBFormat.h"
16
19
  #import "XCUIElementQuery+AMHelpers.h"
17
20
 
21
+ /**
22
+ Collects the hashes of snapshot tree nodes whose identifier equals the given value.
23
+
24
+ @return YES if the caller should stop descending, which happens as soon as a match is
25
+ found while only the first one is wanted
26
+ */
27
+ static BOOL AMCollectIdentifierMatches(id<XCUIElementSnapshot> snapshot,
28
+ NSString *accessibilityId,
29
+ BOOL firstMatchOnly,
30
+ NSMutableSet<NSString *> *matchedHashes)
31
+ {
32
+ if (nil == snapshot) {
33
+ return NO;
34
+ }
35
+ if ([[AMSnapshotUtils wdIdentifierWithSnapshot:snapshot] isEqualToString:accessibilityId]) {
36
+ [matchedHashes addObject:[AMSnapshotUtils hashWithSnapshot:snapshot]];
37
+ if (firstMatchOnly) {
38
+ return YES;
39
+ }
40
+ }
41
+ for (id<XCUIElementSnapshot> child in snapshot.children) {
42
+ if (AMCollectIdentifierMatches(child, accessibilityId, firstMatchOnly, matchedHashes)) {
43
+ return YES;
44
+ }
45
+ }
46
+ return NO;
47
+ }
48
+
49
+ @interface XCUIElement (FBFindPrivate)
50
+
51
+ - (NSArray<XCUIElement *> *)am_descendantsMatchingDomIdentifier:(NSString *)accessibilityId
52
+ shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch;
53
+
54
+ @end
55
+
18
56
  @implementation XCUIElement (FBFind)
19
57
 
20
58
  + (NSArray<XCUIElement *> *)fb_extractMatchingElementsFromQuery:(XCUIElementQuery *)query
@@ -95,7 +133,32 @@
95
133
  XCUIElementQuery *query = [[self descendantsMatchingType:XCUIElementTypeAny] matchingIdentifier:accessibilityId];
96
134
  [result addObjectsFromArray:[self.class fb_extractMatchingElementsFromQuery:query
97
135
  shouldReturnAfterFirstMatch:shouldReturnAfterFirstMatch]];
98
- return result.copy;
136
+ if (result.count > 0
137
+ || !FBConfiguration.sharedConfiguration.useDomIdAsAccessibilityId) {
138
+ return result.copy;
139
+ }
140
+ // WebKit leaves the standard accessibility identifier of web nodes empty, so the
141
+ // above query cannot match them. Retry through their DOM identifiers.
142
+ return [self am_descendantsMatchingDomIdentifier:accessibilityId
143
+ shouldReturnAfterFirstMatch:shouldReturnAfterFirstMatch];
144
+ }
145
+
146
+ - (NSArray<XCUIElement *> *)am_descendantsMatchingDomIdentifier:(NSString *)accessibilityId
147
+ shouldReturnAfterFirstMatch:(BOOL)shouldReturnAfterFirstMatch
148
+ {
149
+ // Walk a single root snapshot in memory and only resolve the matching nodes back
150
+ // to elements. Binding every descendant up front and snapshotting each one
151
+ // separately exceeds the request timeout on large web trees.
152
+ id<XCUIElementSnapshot> rootSnapshot = [self snapshotWithError:nil];
153
+ if (nil == rootSnapshot) {
154
+ return @[];
155
+ }
156
+ NSMutableSet<NSString *> *matchedHashes = [NSMutableSet set];
157
+ AMCollectIdentifierMatches(rootSnapshot, accessibilityId, shouldReturnAfterFirstMatch, matchedHashes);
158
+ return [AMSnapshotUtils elementsWithHashes:matchedHashes.copy
159
+ rootElement:self
160
+ rootSnapshot:rootSnapshot
161
+ includeOnlyFirstMatch:shouldReturnAfterFirstMatch];
99
162
  }
100
163
 
101
164
  @end
@@ -238,6 +238,7 @@ const static NSString *CAPABILITIES_KEY = @"capabilities";
238
238
  AM_BOUND_ELEMENTS_BY_INDEX_SETTING: @(FBSession.activeSession.boundElementsByIndex),
239
239
  AM_USE_DEFAULT_UI_INTERRUPTIONS_HANDLING_SETTING: @(!application.am_doesNotHandleUIInterruptions),
240
240
  AM_FETCH_FULL_TEXT: @(FBConfiguration.sharedConfiguration.fetchFullText),
241
+ AM_USE_DOM_ID_AS_ACCESSIBILITY_ID: @(FBConfiguration.sharedConfiguration.useDomIdAsAccessibilityId),
241
242
  }
242
243
  );
243
244
  }
@@ -256,6 +257,10 @@ const static NSString *CAPABILITIES_KEY = @"capabilities";
256
257
  if (nil != [settings objectForKey:AM_FETCH_FULL_TEXT]) {
257
258
  FBConfiguration.sharedConfiguration.fetchFullText = [settings objectForKey:AM_FETCH_FULL_TEXT];
258
259
  }
260
+ if (nil != [settings objectForKey:AM_USE_DOM_ID_AS_ACCESSIBILITY_ID]) {
261
+ FBConfiguration.sharedConfiguration.useDomIdAsAccessibilityId =
262
+ [[settings objectForKey:AM_USE_DOM_ID_AS_ACCESSIBILITY_ID] boolValue];
263
+ }
259
264
 
260
265
  return [self handleGetSettings:request];
261
266
  }
@@ -28,4 +28,7 @@ extern NSString* const AM_USE_DEFAULT_UI_INTERRUPTIONS_HANDLING_SETTING;
28
28
  /*! Whether to use custom snapshotting mechanism to fetch full element's text payload instead of the first 512 chars */
29
29
  extern NSString* const AM_FETCH_FULL_TEXT;
30
30
 
31
+ /*! Whether to expose a WKWebView web element's DOM id as its accessibility id */
32
+ extern NSString* const AM_USE_DOM_ID_AS_ACCESSIBILITY_ID;
33
+
31
34
  NS_ASSUME_NONNULL_END
@@ -19,3 +19,4 @@
19
19
  NSString* const AM_BOUND_ELEMENTS_BY_INDEX_SETTING = @"boundElementsByIndex";
20
20
  NSString* const AM_USE_DEFAULT_UI_INTERRUPTIONS_HANDLING_SETTING = @"useDefaultUiInterruptionsHandling";
21
21
  NSString* const AM_FETCH_FULL_TEXT = @"fetchFullText";
22
+ NSString* const AM_USE_DOM_ID_AS_ACCESSIBILITY_ID = @"useDomIdAsAccessibilityId";
@@ -30,6 +30,33 @@ NS_ASSUME_NONNULL_BEGIN
30
30
  */
31
31
  + (NSString *)hashWithSnapshot:(id)snapshot;
32
32
 
33
+ /**
34
+ Retrieves the identifier WebDriver clients should observe for the given snapshot.
35
+ This is the standard accessibility identifier, except for WebKit (WKWebView) web
36
+ nodes, which leave it empty and publish their HTML `id` through the non-standard
37
+ AXDOMIdentifier attribute instead. That one is only consulted while the
38
+ `useDomIdAsAccessibilityId` setting is enabled, so a native identifier is never
39
+ overridden.
40
+
41
+ @param snapshot snapshot instance to retrieve the identifier for
42
+ @return The identifier value or nil
43
+ */
44
+ + (nullable NSString *)wdIdentifierWithSnapshot:(nullable id<XCUIElementSnapshot>)snapshot;
45
+
46
+ /**
47
+ Resolves snapshot hashes back to live elements below the given root element.
48
+
49
+ @param hashes snapshot hashes to resolve, as returned by hashWithSnapshot:
50
+ @param rootElement the element the hashes were collected from
51
+ @param rootSnapshot the snapshot of rootElement
52
+ @param firstMatch whether to only return the first matching element
53
+ @return The matching elements. Could be empty
54
+ */
55
+ + (NSArray<XCUIElement *> *)elementsWithHashes:(NSSet<NSString *> *)hashes
56
+ rootElement:(XCUIElement *)rootElement
57
+ rootSnapshot:(id<XCUIElementSnapshot>)rootSnapshot
58
+ includeOnlyFirstMatch:(BOOL)firstMatch;
59
+
33
60
  @end
34
61
 
35
62
  NS_ASSUME_NONNULL_END
@@ -16,6 +16,66 @@
16
16
 
17
17
  #import "AMSnapshotUtils.h"
18
18
 
19
+ #import <ApplicationServices/ApplicationServices.h>
20
+ #import <dlfcn.h>
21
+
22
+ #import "FBConfiguration.h"
23
+ #import "FBLogger.h"
24
+ #import "XCUIElementQuery+AMHelpers.h"
25
+
26
+ static NSString *const AM_DOM_IDENTIFIER_ATTRIBUTE = @"AXDOMIdentifier";
27
+
28
+ /**
29
+ Creates an AXUIElementRef from an accessibility element's remote token. This entry
30
+ point is not exported in the public link stubs, so it is resolved dynamically. The
31
+ underscore-prefixed name is the one actually present in the framework.
32
+ */
33
+ typedef AXUIElementRef _Nullable (*AMCreateWithRemoteTokenFn)(CFDataRef);
34
+
35
+ static AMCreateWithRemoteTokenFn AMRemoteTokenFn(void)
36
+ {
37
+ static AMCreateWithRemoteTokenFn fn = NULL;
38
+ static dispatch_once_t onceToken;
39
+ dispatch_once(&onceToken, ^{
40
+ fn = (AMCreateWithRemoteTokenFn)(dlsym(RTLD_DEFAULT, "_AXUIElementCreateWithRemoteToken")
41
+ ?: dlsym(RTLD_DEFAULT, "AXUIElementCreateWithRemoteToken"));
42
+ });
43
+ return fn;
44
+ }
45
+
46
+ /**
47
+ Whether this process may use the public Accessibility API. XCUITest itself does not
48
+ need it, so an untrusted runner keeps automating native content normally while every
49
+ AXDOMIdentifier read fails with kAXErrorAPIDisabled. Guarding the read turns a failing
50
+ syscall per element into a cached boolean. TCC trust is fixed at process start, so the
51
+ answer cannot change under us.
52
+ */
53
+ static BOOL AMIsAccessibilityTrusted(void)
54
+ {
55
+ static BOOL isTrusted = NO;
56
+ static dispatch_once_t onceToken;
57
+ dispatch_once(&onceToken, ^{
58
+ isTrusted = AXIsProcessTrusted();
59
+ if (!isTrusted) {
60
+ [FBLogger log:@"This process is not trusted for the Accessibility API, so WebKit DOM "
61
+ @"identifiers cannot be resolved. Grant Accessibility permission to the "
62
+ @"WebDriverAgentRunner app and restart it to enable useDomIdAsAccessibilityId."];
63
+ }
64
+ });
65
+ return isTrusted;
66
+ }
67
+
68
+ @interface AMSnapshotUtils ()
69
+
70
+ /**
71
+ Reads the non-standard AXDOMIdentifier attribute, where WebKit publishes a web
72
+ node's HTML `id`. Returns nil for native elements, which report
73
+ kAXErrorAttributeUnsupported for it.
74
+ */
75
+ + (nullable NSString *)domIdentifierWithSnapshot:(nullable id)snapshot;
76
+
77
+ @end
78
+
19
79
  @implementation AMSnapshotUtils
20
80
 
21
81
  + (NSString *)hashWithSnapshot:(id)snapshot
@@ -24,4 +84,79 @@
24
84
  return [token base64EncodedStringWithOptions:0];
25
85
  }
26
86
 
87
+ + (nullable NSString *)wdIdentifierWithSnapshot:(nullable id<XCUIElementSnapshot>)snapshot
88
+ {
89
+ NSString *identifier = snapshot.identifier;
90
+ if (identifier.length > 0
91
+ || !FBConfiguration.sharedConfiguration.useDomIdAsAccessibilityId) {
92
+ return identifier;
93
+ }
94
+ return [self domIdentifierWithSnapshot:snapshot] ?: identifier;
95
+ }
96
+
97
+ + (NSArray<XCUIElement *> *)elementsWithHashes:(NSSet<NSString *> *)hashes
98
+ rootElement:(XCUIElement *)rootElement
99
+ rootSnapshot:(id<XCUIElementSnapshot>)rootSnapshot
100
+ includeOnlyFirstMatch:(BOOL)firstMatch
101
+ {
102
+ if (0 == hashes.count) {
103
+ return @[];
104
+ }
105
+
106
+ NSMutableArray<XCUIElement *> *matchingElements = [NSMutableArray array];
107
+ if ([hashes containsObject:[self hashWithSnapshot:rootSnapshot]]) {
108
+ [matchingElements addObject:rootElement];
109
+ if (firstMatch) {
110
+ return matchingElements.copy;
111
+ }
112
+ }
113
+ NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id snapshot, NSDictionary *bindings) {
114
+ return [hashes containsObject:[self hashWithSnapshot:snapshot]];
115
+ }];
116
+ [matchingElements addObjectsFromArray:[[rootElement descendantsMatchingType:XCUIElementTypeAny]
117
+ matchingPredicate:predicate].am_allMatches];
118
+ return firstMatch && matchingElements.count > 0
119
+ ? @[matchingElements.firstObject]
120
+ : matchingElements.copy;
121
+ }
122
+
123
+ + (nullable NSString *)domIdentifierWithSnapshot:(nullable id)snapshot
124
+ {
125
+ if (nil == snapshot || !AMIsAccessibilityTrusted()) {
126
+ return nil;
127
+ }
128
+ AMCreateWithRemoteTokenFn createRef = AMRemoteTokenFn();
129
+ if (NULL == createRef) {
130
+ return nil;
131
+ }
132
+
133
+ @try {
134
+ NSData *token = [[snapshot valueForKey:@"_accessibilityElement"] valueForKey:@"_token"];
135
+ if (![token isKindOfClass:NSData.class]) {
136
+ return nil;
137
+ }
138
+ AXUIElementRef ref = createRef((__bridge CFDataRef)token);
139
+ if (NULL == ref) {
140
+ return nil;
141
+ }
142
+ CFTypeRef value = NULL;
143
+ AXError err = AXUIElementCopyAttributeValue(ref,
144
+ (__bridge CFStringRef)AM_DOM_IDENTIFIER_ATTRIBUTE,
145
+ &value);
146
+ CFRelease(ref);
147
+ if (kAXErrorSuccess != err || NULL == value) {
148
+ return nil;
149
+ }
150
+ NSString *result = [(__bridge id)value isKindOfClass:NSString.class]
151
+ ? [(__bridge NSString *)value copy]
152
+ : nil;
153
+ CFRelease(value);
154
+ return result.length > 0 ? result : nil;
155
+ } @catch (NSException *e) {
156
+ // Only the private _accessibilityElement._token key path may throw here
157
+ [FBLogger logFmt:@"Cannot retrieve the DOM identifier of a snapshot. Original error: %@", e.reason];
158
+ return nil;
159
+ }
160
+ }
161
+
27
162
  @end
@@ -33,6 +33,9 @@ NS_ASSUME_NONNULL_BEGIN
33
33
  /*! Whether to use custom snapshotting mechanism to fetch full element's text payload instead of the first 512 chars */
34
34
  @property BOOL fetchFullText;
35
35
 
36
+ /*! Whether to expose a WKWebView web element's DOM id as its accessibility id */
37
+ @property BOOL useDomIdAsAccessibilityId;
38
+
36
39
  /**
37
40
  The range of ports that the HTTP Server should attempt to bind on launch
38
41
  */
@@ -12,6 +12,7 @@
12
12
  static NSUInteger const DefaultStartingPort = 10100;
13
13
  static NSUInteger const DefaultPortRange = 100;
14
14
  static BOOL FBFetchFullText = NO;
15
+ static BOOL FBUseDomIdAsAccessibilityId = NO;
15
16
 
16
17
  @implementation FBConfiguration
17
18
 
@@ -86,6 +87,16 @@ static FBConfiguration *instance;
86
87
  FBFetchFullText = fetchFullText;
87
88
  }
88
89
 
90
+ - (BOOL)useDomIdAsAccessibilityId
91
+ {
92
+ return FBUseDomIdAsAccessibilityId;
93
+ }
94
+
95
+ - (void)setUseDomIdAsAccessibilityId:(BOOL)useDomIdAsAccessibilityId
96
+ {
97
+ FBUseDomIdAsAccessibilityId = useDomIdAsAccessibilityId;
98
+ }
99
+
89
100
  - (NSRange)bindingPortRange
90
101
  {
91
102
  // 'WebDriverAgent --port 8080' can be passed via the arguments to the process
@@ -184,7 +184,7 @@ static NSString *const kXMLIndexPathKey = @"private_indexPath";
184
184
  return @[];
185
185
  }
186
186
 
187
- NSMutableArray<NSString *> *hashes = [NSMutableArray array];
187
+ NSMutableSet<NSString *> *hashes = [NSMutableSet set];
188
188
  for (NSXMLNode *node in nodes) {
189
189
  if (![node isKindOfClass:NSXMLElement.class]) {
190
190
  continue;
@@ -195,21 +195,10 @@ static NSString *const kXMLIndexPathKey = @"private_indexPath";
195
195
  }
196
196
  [hashes addObject:attrValue];
197
197
  }
198
- NSMutableArray<XCUIElement *> *matchingElements = [NSMutableArray array];
199
- NSString *selfHash = [AMSnapshotUtils hashWithSnapshot:rootSnapshot];
200
- if ([hashes containsObject:selfHash]) {
201
- [matchingElements addObject:rootElement];
202
- if (firstMatch) {
203
- return matchingElements.copy;
204
- }
205
- }
206
- NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id snapshot, NSDictionary *bindings) {
207
- return [hashes containsObject:[AMSnapshotUtils hashWithSnapshot:snapshot]];
208
- }];
209
- [matchingElements addObjectsFromArray:[[rootElement descendantsMatchingType:XCUIElementTypeAny] matchingPredicate:predicate].am_allMatches];
210
- return firstMatch && matchingElements.count > 0
211
- ? @[matchingElements.firstObject]
212
- : matchingElements.copy;
198
+ return [AMSnapshotUtils elementsWithHashes:hashes.copy
199
+ rootElement:rootElement
200
+ rootSnapshot:rootSnapshot
201
+ includeOnlyFirstMatch:firstMatch];
213
202
  }
214
203
 
215
204
  + (NSXMLDocument *)xmlRepresentationWithSnapshot:(id<XCUIElementSnapshot>)root
@@ -366,7 +355,7 @@ static NSString *const FBAbstractMethodInvocationException = @"AbstractMethodInv
366
355
 
367
356
  + (NSString *)valueForElement:(id<XCUIElementSnapshot>)element
368
357
  {
369
- return element.identifier;
358
+ return [AMSnapshotUtils wdIdentifierWithSnapshot:element];
370
359
  }
371
360
 
372
361
  @end
@@ -1367,6 +1367,7 @@
1367
1367
  GCC_WARN_UNUSED_FUNCTION = YES;
1368
1368
  GCC_WARN_UNUSED_VARIABLE = YES;
1369
1369
  MACOSX_DEPLOYMENT_TARGET = 10.15;
1370
+ "MACOSX_DEPLOYMENT_TARGET[sdk=macosx27.*]" = 12.0;
1370
1371
  MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
1371
1372
  MTL_FAST_MATH = YES;
1372
1373
  ONLY_ACTIVE_ARCH = YES;
@@ -1420,6 +1421,7 @@
1420
1421
  GCC_WARN_UNUSED_FUNCTION = YES;
1421
1422
  GCC_WARN_UNUSED_VARIABLE = YES;
1422
1423
  MACOSX_DEPLOYMENT_TARGET = 10.15;
1424
+ "MACOSX_DEPLOYMENT_TARGET[sdk=macosx27.*]" = 12.0;
1423
1425
  MTL_ENABLE_DEBUG_INFO = NO;
1424
1426
  MTL_FAST_MATH = YES;
1425
1427
  SDKROOT = macosx;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "appium-mac2-driver",
3
- "version": "4.0.7",
3
+ "version": "4.1.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "appium-mac2-driver",
9
- "version": "4.0.7",
9
+ "version": "4.1.1",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@appium/strongbox": "^1.0.0-rc.1",
@@ -492,9 +492,9 @@
492
492
  }
493
493
  },
494
494
  "node_modules/bare-url": {
495
- "version": "2.4.7",
496
- "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.7.tgz",
497
- "integrity": "sha512-o8CRCiJtib+ycO3mE4A5UChtGX4dDP2XxsWVu9P+Zc3H8tcmKwNVEDoDTXmwN+uuMhfKeT7/i7Y26xS8W7ohoA==",
495
+ "version": "2.5.1",
496
+ "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.1.tgz",
497
+ "integrity": "sha512-cD5ciQuKlx+eumTCfqbfiL+fhQm+dHbVNB/cX4+d+I/nx4JsVop9VoFEPAs5RJ6I84QR6bZIBjpd2kjDOYeWcg==",
498
498
  "license": "Apache-2.0",
499
499
  "dependencies": {
500
500
  "bare-path": "^3.0.0"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "appium-mac2-driver",
3
- "version": "4.0.7",
3
+ "version": "4.1.1",
4
4
  "description": "XCTest-based Appium driver for macOS apps automation",
5
5
  "keywords": [
6
6
  "XCTest",