-
-
Save ngtk/7d4a92ad506df7f9a36f75f6c63ec835 to your computer and use it in GitHub Desktop.
| import UIKit | |
| /** | |
| `Accessible` provides the way to find the element by a consistent name | |
| in UI tests. | |
| `generateAccessibilityIdentifiers()` creates the identifier that forms | |
| the combination of the class name and the property name that is styled | |
| like "ClassName.propertyName" and sets it to `accessibilityIdentifier` | |
| to each property that is UIView. | |
| e.g. | |
| - `WelcomeViewController.view` | |
| - `WelcomeViewController.buttonStackView` | |
| You can conform `Accessible` to your custom `UIViewController` or `UIView` | |
| and call `generateAccessibilityIdentifiers()` after all view properties | |
| are initalized like in the last line of `viewDidLoad()` or an initalizer. | |
| **/ | |
| public protocol Accessible { | |
| func generateAccessibilityIdentifiers() | |
| } | |
| public extension Accessible where Self: NSObject { | |
| func generateAccessibilityIdentifiers() { | |
| #if DEBUG | |
| let mirror = Mirror(reflecting: self) | |
| generateAccessibilityIdentifiers(mirror) | |
| #endif | |
| } | |
| private func generateAccessibilityIdentifiers(_ mirror: Mirror) { | |
| // If it has no children, we treat the object as objc implementation. | |
| if mirror.children.isEmpty { | |
| generateAccessibilityIdentifiersForObjc(mirror) | |
| } else { | |
| for child in mirror.children { | |
| generateAccessibilityIdentifier(for: child) | |
| } | |
| } | |
| if let superclassMirror = mirror.superclassMirror { | |
| generateAccessibilityIdentifiers(superclassMirror) | |
| } | |
| } | |
| private func generateAccessibilityIdentifier(for child: Mirror.Child) { | |
| guard let view = child.value as? UIView else { return } | |
| guard let label = child.label else { return } | |
| let property = label.replacingOccurrences(of: ".storage", with: "") // lazy vars appends ".storage". | |
| view.accessibilityIdentifier = "\(type(of: self)).\(property)" | |
| } | |
| private func generateAccessibilityIdentifiersForObjc(_ mirror: Mirror) { | |
| var outCount: UInt32 = 0 | |
| guard let properties = class_copyPropertyList(mirror.subjectType as? AnyClass, &outCount) else { return } | |
| // swiftlint:disable:next identifier_name | |
| for i: UInt32 in 0 ..< outCount { | |
| let property = properties[Int(i)] | |
| let ignoreProperties = [ | |
| "window", // Weak referenced as the property of a view controller. | |
| "viewIfLoaded", // `view` has been overridden by this property. | |
| ] | |
| if | |
| let name = property.name(), | |
| !ignoreProperties.contains(name), | |
| property.type() is UIView.Type, | |
| let view = value(forKey: name) as? UIView { | |
| view.accessibilityIdentifier = "\(type(of: self)).\(name)" | |
| } | |
| } | |
| } | |
| } | |
| private extension objc_property_t { | |
| func name() -> String? { | |
| return String(cString: property_getName(self), encoding: .utf8) | |
| } | |
| func type() -> AnyClass? { | |
| guard let attributes = String(cString: property_getAttributes(self)!, encoding: .utf8) | |
| else { return nil } | |
| guard let regexp = try? NSRegularExpression(pattern: "T@\"(.*)\"", options: []) else { return nil } | |
| guard let result = regexp.firstMatch( | |
| in: attributes, | |
| options: [], | |
| range: NSRange(location: 0, length: attributes.count) | |
| ) else { return nil } | |
| let range = result.range(at: 1) | |
| let className = attributes[range] | |
| return NSClassFromString(className) | |
| } | |
| } |
Hello, on iOS13 this protocol throws exception
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Project.ViewController 0x7ff2cf714870> valueForUndefinedKey:]: this class is not key value coding-compliant for the key tabBarObservedScrollView.' particularly in line 72. It's strange bc i have only one VC which is not a tabBarVC, it's an empty project and still throws that error. Do you know what might causing this problem, or how to fix it? Btw. thanks for sharing code
EDIT: quick fix between line 72 and 71 name != "tabBarObservedScrollView" and it's working. I think this might be a bug bc tabBarObservedScrollView applies to tvOS 13+ so why it's there idk
Sorry, but, I don't have any idea. I'll try this later on iOS 13.
環境差異ぽいですが、より新しくはそうですね。コメントありがとうございます!😊