Simplify SwiftUI accessibility with one modifier to rule them all!

When I was writing my tutorial Accessible SwiftUI for easy UI testing, I mentioned that it would be possible to combine the various accessibility attributes you can add to a View into one ViewModifier. Currently, if you want to add an accessibility label, identifier, value, and hint to your Views, you need to add four separate modifiers every time. You have to type the lengthy word ‘accessibility’ every time, which can slow you down.

There is a better way!

In UIKit you may have discovered that you can’t set myView.frame.height, because height in that case is a get-only property. That means you have to set myView.frame to something like CGRect(x: myView.frame.origin.x, y: myView.frame.origin.y, width: myView.frame.width, height: 50). Miss any of these parameters, and you fail to construct a CGRect. Fail to construct a CGRect, and you can’t set the UIView’s frame. SwiftUI has a better way to do most things, and customizing specific aspects of a View is no exception.

The standard ViewModifier constructor requires you to pass in a value for every uninitialized property. This would make it pretty difficult for us to only pass in the accessibility strings we want. In the Hacking With Swift Custom Modifiers tutorial, Paul Hudson shows how we can create View extensions that neatly wrap our ViewModifier in a shorter identifier.

This allows us to decide which parameters we want to be optional (by providing a default value) or required (by not providing a default value).

See more of what our Digital team does by visiting the Twinkl Reality page

A11yModifier

Be careful when using a11y elsewhere, however, as there are a few ways in which A11y is not accessible.

Let’s create our custom modifier. It is not possible to put a modifier inside an if statement, so you can’t avoid adding a label if the label string was nil for instance. Instead, I’ve used the nil coalescing operator ?? to give an empty string when the property is nil.

Other than the main strings for identifiers, labels, values, and hints, I’ve also added a Boolean value that determines whether an element is hidden from VoiceOver.

Not everything needs to be accessible if it doesn’t add useful context for what is being displayed

Although the properties are optional that are not initialized, they shouldn’t ever be nil when we use the View extension a11y because all of the parameters have default values. It’s easy to see how we could set these defaults to something we want all Views to have, but that would probably be a mistake. Something generic in the label like ‘Button’ would not help VoiceOver at all and would add unnecessary noise, slowing the user down.

Just like the .frame(height: 50) modifier I mentioned, giving default values for the parameters gives us the choice of adding them or not. In the example above, I add different combinations of accessibility attributes and Xcode doesn’t give an error because I didn’t set them all.

Note that the fact that these values are set to an empty string by default overwrites the defaults.

Forcing accessibility conformance

Next steps

Let me know if you have any other ideas in a response below!

About Rob Sturgeon

Rob is a Placement App Developer at Twinkl. He’s currently working on their iOS and Android apps, enabling teachers to view, organize and download resources on the go.

An iOS developer who writes about gadgets, startups and cryptocurrencies. Swift programming tutorials and SwiftUI documentation are here too. robsturgeon.com