How can I set accessibilityIdentifier to UIAlertController?
Asked Answered
S

4

25

This is how I simply create UIAlertController and present it on the screen:

private class func showAlertWithTitle(title: String, message: String) {

    let alert = UIAlertController(title: title, message: message, preferredStyle: .Alert)
    //alert.accessibilityLabel = "my string here"      //doesnt work
    let action = UIAlertAction(title: "OK", style: .Default) { action in
        alert.dismissViewControllerAnimated(true, completion: nil)
    }

    alert.addAction(action)
    UIStoryboard.topViewController()?.presentViewController(alert, animated: true, completion: nil)
}

and this is how I access it under UITests:

emailAlert = app.alerts["First Name"] //for title "First Name"

but I would like to set there custom identifier and access this by firstName like this:

emailAlert = app.alerts["firstName"]

Is it possible?

Saltpeter answered 30/6, 2016 at 8:10 Comment(0)
S
15

This is an old thread but someone might use this.

I was able to set the accessibility identifier like this:

let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.view.accessibilityIdentifier = "custom_alert"
alert.view.accessibilityValue = "\(title)-\(message)"

alert.addAction(
    UIAlertAction(
        title: "ALERT_BUTTON_OK".localized,
        style: .default,
        handler: handler
    )
)

present(alert, animated: true)

That way I can access the alert by accessibility identifier and check its contents in accessibility value.

It is not perfect of course, but it works - at least for my testing using Appium.

Sputter answered 5/6, 2018 at 15:5 Comment(2)
I've been looking for a way to give an alert a specific identifier. This was the answer I've been looking for. Thanks! Oh, and it works fine in Swift 4.Thoer
You write that you check its contents, but how do you check the text of the button? It has no accessibility id.Doubloon
H
6

The only way I figured out to do this was to use Apple's private APIs. You call valueForKey on the UIAlertAction object with this super secret key: "__representer" to get whats called a _UIAlertControllerActionView.

    let alertView = UIAlertController(title: "This is Alert!", message: "This is a message!", preferredStyle: .Alert)
    let okAction = UIAlertAction(title: "OK", style: .Default, handler: nil)

    alertView.addAction(okAction)

    self.presentViewController(alertView, animated: true, completion: {
        let alertButton = action.valueForKey("__representer")
        let view = alertButton as? UIView
        view?.accessibilityIdentifier = "okAction_AID"
    })

This has to be done in the completion handler because that that _UIAlertControllerActionView won't exist until the view is presented. On a side note in my project I used these following extensions to make things easier / more readable:

extension UIAlertController {
    func applyAccessibilityIdentifiers()
    {
        for action in actions
        {
            let label = action.valueForKey("__representer")
            let view = label as? UIView
            view?.accessibilityIdentifier = action.getAcAccessibilityIdentifier()
        }

    }

}

extension UIAlertAction
{
    private struct AssociatedKeys {
        static var AccessabilityIdentifier = "nsh_AccesabilityIdentifier"
    }

    func setAccessibilityIdentifier(accessabilityIdentifier: String)
    {
        objc_setAssociatedObject(self, &AssociatedKeys.AccessabilityIdentifier, accessabilityIdentifier, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }

    func getAcAccessibilityIdentifier() -> String?
    {
        return objc_getAssociatedObject(self, &AssociatedKeys.AccessabilityIdentifier) as? String
    }
}

So the above code would be rewritten:

    let alertView = UIAlertController(title: NSLocalizedString("NMN_LOGINPAGECONTROLLER_ERROR_TITLE", comment: ""), message: message as String, preferredStyle:.Alert)
    let okAction = UIAlertAction(title: NSLocalizedString("NMN_OK", comment: ""), style: .Default, handler: nil)
    okAction.setAccessibilityIdentifier(InvalidLoginAlertView_AID)


    alertView.addAction(okAction)


    self.presentViewController(alertView, animated: true, completion: {
        alertView.applyAccessibilityIdentifiers()
    })

My first attempt involved trying to navigate the view hierarchy but that became difficult since UIAlertControllerActionView was not a part of the public API. Anyway I'd probably would try to ifdef out the valueForKey("__representer") for builds submitted for the app store or Apple might give you a spanking.

Haletky answered 19/11, 2016 at 1:3 Comment(7)
This worked. I just opened a bug with Apple in the hope they would do something if many of us complained.Unheard
Any idea how to set the accessibility identifier for the title and the message though?Unheard
Sorry I'm not sure. Only needed to set accessibility identifiers for automation purposes so never had the need to access the title or message.Haletky
Thanks @AnthonyOlds ! its worked for me. but I have one question why you used " __representer" ? is there any documentation for it?Newt
I think these keys are undocumented but I learned that this specific key would work from the accepted answer in this stack overflow question: #26461206Haletky
hacking into the apple api by using valueForKey provides the highest chance of getting rejected when applying for reviewSwivel
Nowadays you could probably just use: action.accessibilityIdentifier since UIAlertAction now extends UIAccessibilityIdentificationSmegma
D
0

Right now I have a UIAlertAction called addCamera and I'm just doing:

addCamera.accessibilityLabel = "camera-autocomplete-action-photo"

That allows me to tap it in UI Tests as follows:

app.sheets.buttons["camera-autocomplete-action-photo"].firstMatch.tap()

Doorstone answered 5/11, 2018 at 11:5 Comment(2)
That's extremely unhelpful towards those who actually need accessibility help.Recension
You must not use "programming named" labels for testing, as they are read by users with accessibility enabled, instead use accesibility identifiers.Momently
E
-1

From Apple docs...

https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/UIKitUICatalog/UIAlertView.html

Making Alert Views Accessible

Alert views are accessible by default. Accessibility for alert views pertains to the alert title, alert message, and button titles. If VoiceOver is activated, it speaks the word “alert” when an alert is shown, then speaks its title followed by its message if set. As the user taps a button, VoiceOver speaks its title and the word “button.” As the user taps a text field, VoiceOver speaks its value and “text field” or “secure text field.”

Espionage answered 29/6, 2017 at 17:15 Comment(5)
This does not mention how to set accessibility identifiers at all.Unheard
No it doesn't, but it's Apple's recommended way of accessing Alerts.Espionage
well this sucks especially if there are localizations involved in the process.Taveda
That text talks about making the alerts accessible to users, which is only slightly related to using an accessibilityIdentifier to access them for UI testing. An accessibilityIdentifier is not really related to the other accessibility* items except by name similarity; it's not used at all by users.Doubloon
This is talking about UIAlertViews and not UIAlertController, it has nothing to do with the questionHyohyoid

© 2022 - 2024 — McMap. All rights reserved.