Is there a way to find if the XCUIElement has focus or not?
Asked Answered
E

5

25

One of my screen has multiple text fields, I can land to this screen from different other screens. In each case I am making one or another text field as first responder. I am not able to write test to determine whether the desired textField has focus or not.

When I print the textfield in console -

Output: { TextField 0x131171d40: traits: 146031247360, Focused, {{6.0, 108.3}, {402.0, 35.0}}, value: ​ }

But I could not find any focus/isFocus property on XCUIElement.

Is there a way to achieve this ?

Extenuatory answered 1/10, 2015 at 22:21 Comment(3)
XCUIElement seems to derive some of its properties from the accessibility traits, but does not necessarily expose the traits. Have you tried the selected property to see if that gives you what you want?Jubbulpore
selected property is returning false/nil for my textFields.Extenuatory
There is property hasKeyboardFocus, please see my answer bellow.Anissa
A
30

I little bit late for the party :) However as far as I can see from dumping the variable XCUIElement it has one interesting property:

property name: hasKeyboardFocus

property type: TB,R

So you can check if your element has focus the following way:

let hasFocus = (yourTextField.value(forKey: "hasKeyboardFocus") as? Bool) ?? false

NB: you can dump the property variables of any NSObject sublass with following extension:

extension NSObject {
    func dumpProperties() {
        var outCount: UInt32 = 0

        let properties = class_copyPropertyList(self.dynamicType, &outCount)
        for index in 0...outCount {
            let property = properties[Int(index)]
            if nil == property {
                continue
            }
            if let propertyName = String.fromCString(property_getName(property)) {
                print("property name: \(propertyName)")
            }
            if let propertyType = String.fromCString(property_getAttributes(property)) {
                print("property type: \(propertyType)")
            }
        }
    }
}

Update: Properties dump, Swift 4:*

extension NSObject {
    func dumpProperties() {
        var outCount: UInt32 = 0

        let properties = class_copyPropertyList(type(of: self), &outCount)
        for index in 0...outCount {
            guard let property = properties?[Int(index)] else {
                continue
            }
            let propertyName = String(cString: property_getName(property))
            print("property name: \(propertyName)")
            guard let propertyAttributes = property_getAttributes(property) else {
                continue
            }
            let propertyType = String(cString: propertyAttributes)
            print("property type: \(propertyType)")
        }
    }
}
Anissa answered 10/3, 2016 at 11:50 Comment(7)
Fantastic find, thanks for responding with this answer!Kristopher
brilliant answer! if you like to wait for element with focus just use predicate and expectation let focusPredicate = NSPredicate(format: "exists == true && hasKeyboardFocus == true")Garden
Excellent find!! Apple's documentation states "The XCUIElementAttributes protocol, as adopted by XCUIElement, provides additional properties for querying the current state of a UI element's attributes." That protocol has a 'hasFocus' variable, however when trying to actually use this, you get the "XCUIElement has no member 'hasFocus'" message from xcode. Thanks again!Noisette
@Anissa How did you find the property named "hasKeyboardFocus"? Even Apple's documentation does not shows about it. Can you optimize the above code to Swift 4 and above? It throws the error on the line "self.dynamicType"Annieannihilate
@leanne thanks a lot for the edit! One doubt. What is the meaning for property types? For example, what is the meaning of "TB,R"?Annieannihilate
@Confused: If you option-click on property_getAttributes in Xcode, the Quick Help says "The format of the attribute string is described in Declared Properties in Objective-C Runtime Programming Guide."Itinerant
@Confused: Those references show that a type starts with T, then is followed by an @encode type (B apparently means "A C++ bool or a C99 _Bool". The R, shown in Table 7-1, under Property Type String, apparently means read-only. Interesting...Itinerant
I
14

Based on @hris.to's excellent answer, I put together this little extension (works in Swift 4 as well)...

extension XCUIElement
{
    func hasFocus() -> Bool {
        let hasKeyboardFocus = (self.value(forKey: "hasKeyboardFocus") as? Bool) ?? false
        return hasKeyboardFocus
    }
}
Itinerant answered 5/8, 2018 at 0:35 Comment(2)
could be also a computed variable like var hasFocus: Bool { return ... }Allottee
@Allottee I don't know if I am doing it right, but I always prefer computed properties instead of methods when there is no parameter. ;)Pfeffer
G
0

I don't know but maybe selected property on XCUIElement is what you're looking for. Try it.

Granadilla answered 1/10, 2015 at 22:33 Comment(1)
Didn't work.It is returning false/nil for textFields in my case.Extenuatory
J
0

There is a method on NSObject via an extension (from UIAccessibility.h), which is available on XCUIElement named accessibilityElementIsFocused(), but it seems to not return true even when the debugDescription of that element clearly says Focused. There are a few other related methods accessibilityElementDidLoseFocus() and accessibilityElementDidBecomeFocused(), which appear to be methods intended to be overridden by a subclasses of NSObject to get notified of changes to the Focused state.

After this digging I'm inclined to say that the notion of Focused that we are discussing is not the same as the firstResponder state, which is probably what you're hoping to know. I believe at this point that focus indicates that the element is the focus of some assistive technology like VoiceOver, based on some of the comments for these methods.

If you want to be able to tell if an item is firstResponder directly, I think at this point it's time to file a bug report.

If there is a way to interact directly with the software keyboard, a hacky workaround might be to get an XCUIElement for your UITextField and compare the value of your UITextField element before and after typing with the keyboard (you may want to type a character then a backspace). Having said that, I could not figure out how to get the keyboard directly in a cursory attempt.

Jubbulpore answered 1/10, 2015 at 23:47 Comment(0)
S
0

I have observed some instances where

XCUIApplication().textFields[{index/identifier}].selected

does not return true even when I had my cursor focussed on the text field but I was able to enter text with .typeText(). However, .enabled correctly returns true , It appears 'enabled' value represents that UI element has accessibility enabled and you can interact with it.

Not a clean solution but you can try,

XCUIApplication().textFields.elementMatchingPredicate("predicate")

or .elementMatchingType() to get XCUIElement and write your test block while handling the exception condition with guard statement to throw appropriate error if the textField is not found.

Sheik answered 2/10, 2015 at 14:42 Comment(2)
enable will not help here. Enable tells - Whether or not the element is enabled for user interaction. None of my textField is in disabled state. Did you get my question ? I was asking about how can I validate if my textField is first responder.Extenuatory
copy that.From your question, I read your objective was to determine if the Textfield has focus or not so that you can interact with it.In case you want to verify the current first responder from the UIView is your expected UITextField element, you can check this gist link (gist.github.com/steipete/8737196). A hacky workaround could be to get the first responder UIView title to match against your expected textField title and write your conditional test.Sheik

© 2022 - 2024 — McMap. All rights reserved.