How can I set accessibilityIdentifier to UIAlertController? How can I set accessibilityIdentifier to UIAlertController? ios ios

How can I set accessibilityIdentifier to UIAlertController?

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.

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.

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: