Skip to content

Commit bc3d41b

Browse files
authored
Merge pull request #228 from cashapp/priip/add-identifier-in-accessibility-marker
Add accessibility identifier in AccessibilityMarker
2 parents e527ccc + 24b20ed commit bc3d41b

File tree

1 file changed

+62
-1
lines changed

1 file changed

+62
-1
lines changed

Sources/AccessibilitySnapshot/Core/Swift/Classes/AccessibilityHierarchyParser.swift

+62-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//
1616

1717
import Accessibility
18+
import SwiftUI
1819
import UIKit
1920

2021
public struct AccessibilityMarker {
@@ -37,6 +38,10 @@ public struct AccessibilityMarker {
3738
/// focus.
3839
public var description: String
3940

41+
/// A unique identifier for the element, primarily used in UI tests for locating and interacting with elements.
42+
/// This identifier is not visible to users.
43+
public var identifier: String?
44+
4045
/// A hint that will be read by VoiceOver if focus remains on the element after the `description` is read.
4146
public var hint: String?
4247

@@ -209,6 +214,7 @@ public final class AccessibilityHierarchyParser {
209214

210215
return AccessibilityMarker(
211216
description: description,
217+
identifier: element.object.identifier,
212218
hint: hint,
213219
userInputLabels: userInputLabels,
214220
shape: accessibilityShape(for: element.object, in: root),
@@ -714,7 +720,7 @@ extension UIView {
714720
fileprivate extension NSObject {
715721
var customContent: [(label: String, value: String, isImportant:Bool)] {
716722
// Github runs tests on specific iOS versions against specific versions of Xcode in CI.
717-
// Forward deployment on old versions of Xcode require a compile time check which require diferentiation by swift version rather than iOS SDK.
723+
// Forward deployment on old versions of Xcode require a compile time check which require differentiation by swift version rather than iOS SDK.
718724
// See https://swiftversion.net/ for mapping swift version to Xcode versions.
719725

720726
if #available(iOS 14.0, *) {
@@ -741,6 +747,61 @@ fileprivate extension NSObject {
741747
}
742748
return []
743749
}
750+
751+
var identifier: String? {
752+
// The `accessibilityIdentifier` property is part of the `UIAccessibilityIdentification` protocol,
753+
// distinct from other accessibility properties in UIKit.
754+
if let idProtocol = self as? UIAccessibilityIdentification {
755+
return idProtocol.accessibilityIdentifier
756+
}
757+
758+
// Swift occasionally fails to recognize Objective-C subclasses conforming to `UIAccessibilityIdentification`.
759+
// This is likely due to a Swift bug where Objective-C classes lose their protocol conformance
760+
// when converted to `Any` types for use in accessibility APIs.
761+
// See https://github.com/swiftlang/swift/issues/46456 for details.
762+
763+
// Explicitly check UIKit types that conform to `UIAccessibilityIdentification`:
764+
if let view = self as? UIView {
765+
return view.accessibilityIdentifier
766+
}
767+
if let barItem = self as? UIBarItem {
768+
return barItem.accessibilityIdentifier
769+
}
770+
if let alertAction = self as? UIAlertAction {
771+
return alertAction.accessibilityIdentifier
772+
}
773+
if let menuElement = self as? UIMenuElement {
774+
return menuElement.accessibilityIdentifier
775+
}
776+
if let image = self as? UIImage {
777+
return image.accessibilityIdentifier
778+
}
779+
780+
// Use key-value coding as a fallback to access the `accessibilityIdentifier`.
781+
// This is necessary for SwiftUI views, which are wrapped in a `UIHostingController`
782+
// and don't directly expose an `accessibilityIdentifier`.
783+
if let accessibilityIdentifier = value(forKey: "accessibilityIdentifier") as? String {
784+
return accessibilityIdentifier
785+
}
786+
787+
return nil
788+
}
789+
}
790+
791+
// MARK: -
792+
793+
private extension UIHostingController {
794+
/// Provides access to the `accessibilityIdentifier` of the hosted SwiftUI view.
795+
/// This is necessary because SwiftUI views are wrapped in a `UIHostingController`,
796+
/// and don't directly expose an `accessibilityIdentifier`.
797+
var accessibilityIdentifier: String? {
798+
get {
799+
return view.accessibilityIdentifier
800+
}
801+
set {
802+
view.accessibilityIdentifier = newValue
803+
}
804+
}
744805
}
745806

746807
// MARK: -

0 commit comments

Comments
 (0)