14. Mar 2025iOS

Removing barriers: How to make your app accessible for everyone

Nowadays, mobile apps are our digital extended arm - we use them to get work done, communicate, shop, and entertain ourselves. But what if some users can't access these capabilities simply because the app design doesn't account for their needs?

Andrej JaššoiOS Developer
Ilustrácia osoby s logom GoodRequest namiesto hlavy, ktorá stojí vedľa smartfónu a ikon prístupnosti, ktoré predstavujú prístupný obsah.

Accessibility isn't just a formal requirement - it's a mindset that ensures applications are usable by all, regardless of visual 👁️, motor 👏🏽, auditory 👂🏽 or cognitive 🧠 disabilities. ✨ In this article, we will show a concrete example where we compare an inaccessible and an accessible screen of a mobile app.

🔍 You will learn:

✔️ What makes an app inaccessible and what obstacles it creates 🚧

✔️ What a tweaked, accessible version might look like 💡

✔️ Practical tips you can apply in your projects 🛠️

Whether you're a designer, developer or product manager, understanding these principles will help you create apps that don't segregate, but connect. Let's do it! 🚀

What is the problem?

Let's look at the numbers:

  • User Loss: Approximately 70% of users who encounter online accessibility problems leave the website. 1
  • Accessibility and customer experience: 79% of organisations agree that accessible technologies improve the customer experience. 1
  • Global impact: 1 in 6 people in the world live with a disability, making accessibility essential for a large proportion of the population. 2 4
  • Top accessibility flaws: The most common flaws according to the WCAG (Web Content Accessibility Guidelines) include low contrast text (84%), missing alternative text for images (58%), blank links (50%), and missing buttons in forms (46%). 4

Why is this important for economic activity?

  • Brand loyalty: 96% of customers say customer support is important to their brand loyalty, with accessibility being a significant part of it.1
  • Increasing customer retention: Increasing customer retention rates by just 5% can increase profits by 25% to 95%. 1
  • Brand loyalty: 96% of customers say customer support is important to their brand loyalty, with accessibility being an integral part of it. 1
  • Digital accessibility in organisations: 72% of organisations have a digital accessibility policy and 85% see it as a competitive advantage. 5
  • Accessibility and purchasing decisions: 38,6% of employees reported that customers mentioned product accessibility as a reason for their purchase. 3
  • Market growth: the global web accessibility tools market is expected to reach a value of $893.7 million by 2031. 5
  • Impact of lack of accessibility awareness: 75% of people with disabilities and their families have stopped using a company's services due to a lack of accessibility awareness. 1

Screen input

First of all, let's see what it looks like when you first enter.

Dve porovnávané obrazovky, ktoré zobrazujú stránku s produktom "jablko" - obrazovka vľavo je neprístupná obrazovka a je na nej zvýraznený produkt, vpravo je prístupná obrazovka, ktorá zvýrazňuje prvé tlačidlo "pridať do košíka".

Inaccessible flow (left)

In the image on the left, the first problem can be seen right at the start. The inaccessible screen jumps first when you enter to photo and reads the accessible title, which in this case is omitted, so the Screen Reader only reads "Photo, Space". Since this is a photo from a URL, there is no way to go into assets and name the photo directly there. (If the photo were named apple.jpg, the file name would be used for the unspecified accessible name.) Then, after reading, it jumps right to the heart button under the apple image and reads "Like, Button".

This approach has several shortcomings:

  • Screen Reader completely skips the "Add to Cart" button, which is apparently before the "Like" button. However, if I go back one element, it already reads it. This confuses the user at best, because navigating through the elements on the screen doesn't preserve any logical sequence, and at worst the user won't be able to add an item to the cart at all, because it doesn't occur to him to go back a step after entering it.
  • The screen content jumps around in an unwanted way without any user interaction.
  • The photo description itself adds no value to the user, this element should have either been described within the Screen Reader or ignored completely.

Accessible flow (right)

On the right, you'll notice that once I've accessed the "apple" item, I'm moving on to the item's primary action, and that's adding it to the cart. The Screen Reader reads, "Add to Cart product Apple". This button gives the user full context and knows exactly what to expect when the button is pressed. We achieved this by ignoring the image in the content and adding descriptions of each component dynamically according to the content.

// Modifier applied to the image component.
.accessibilityHidden(true)
               
// Modifier used on the add to cart button component.
.accessibilityLabel("Pridať do košíka produkt \(productName)")

Reading the contents

Let's see what it looks like with the information that is written in the table.

Dve porovnávané obrazovky, ktoré zobrazujú stránku s produktom "jablko"- obrazovke vľavo je neprístupná obrazovka a zobrazuje prvý riadok tabuľky, vpravo je prístupná obrazovka, kde je zvýraznený prvý atribút aj so svojou hodnotou.

Inaccessible flow (left)

As soon as I want to read the contents of the table, I very quickly find that even though visually the table works, when I start cycling through the elements, it reads "Hmotnosť" first and then "Farba" right after that, before moving on to the next row where it reads "125g" and "Červená".

Grid {
    GridRow {
        VStack {
            Text("Hmotnosť")
            Text("125 g")
        }

        VStack {
            Text("Farba")
            Text("Červená")
        }
    }

    GridRow {
        VStack {
            Text("Krajina pôvodu")
            Text("Slovensko")
        }

        VStack {
            Text("Nutričné hodnoty")
            Text("52 kcal")
        }
    }
}

Again, the confusion of the user going through this screen must be immense.

Accessible by flow (Right)

On the right, we have made reading content accessible by combining the title and value in the table into a single accessibility group. This results in the Screen Reader automatically reading, "Hmotnosť: 125g".

Grid {
    GridRow {
        VStack {
            Text("Hmotnosť")
            Text("125 g")
        }
        .accessibilityElement(children: .combine)

        VStack {
            Text("Farba")
            Text("Červená")
        }
        .accessibilityElement(children: .combine)
    }

    GridRow {
        VStack {
            Text("Krajina pôvodu")
            Text("Slovensko")
        }
        .accessibilityElement(children: .combine)


        VStack {
            Text("Nutričné hodnoty")
            Text("52 kcal")
        }
        .accessibilityElement(children: .combine)
    }
}

The integrity of this information will make for a completely different app experience for the user.

Rating

Next, we look at a simple product evaluation component.

Dve porovnávané obrazovky, ktoré zobrazujú stránku s produktom "jablko" - obrazovke vľavo je neprístupná obrazovka, kde je označená prvá hviezdička, vpravo je prístupná obrazovka, ktorá zobrazuje celý riadok s hviezdičkami.

Inaccessible flow (left)

When I move in the inaccessible flow to the rating component, it automatically moves me to the first star as the first action button from the component and the reader will report: "Add to favorites, Button".

When I start scrolling from left to right, it reports the same thing on every star. It is very difficult to impossible for a blind person to figure out what is happening on the screen.

@State var rating: Int = 1

var body: some View {
    HStack {
        ForEach(1..<6) { index in
            Button(action: { rating = index }) {
                Image(systemName: index <= rating ? "star.fill" : "star")
            }
        }
    }
}

Again, the user is confused because they don't know what the buttons do. In the worst case, he thinks they are doing what the reader said: adding to favourites, while he will think he is adding the product to favourites, but in reality he will be sending a rating that does not reflect his real opinion.

Accessible flow (Right)

In the accessible flow, we've grouped the rating component and added the "adjustable" flag to it.

Once we move to the rating in the accessible flow, the reader will say: "Product rating apple 1 out of 5, adjustable". This indicates to the user that:

  • the rating is related to the product apple,
  • the current value is 1 out of 5,
  • he can change its adjustable value by a simple gesture over the component.
@State var rating: Int = 1

var body: some View {
    HStack {
        ForEach(1..<6) { index in
            Button(action: { rating = index }) {
                Image(systemName: index <= rating ? "star.fill" : "star")
            }
        }
    }
    .accessibilityElement()
    .accessibilityLabel(Text("Hodnotenie"))
    .accessibilityValue(Text(String(rating) + " z 5"))
    .accessibilityAdjustableAction { direction in
        switch direction {
        case .increment:
            guard rating < 5 else { break }
            rating += 1
        case .decrement:
            guard rating > 1 else { break }
            rating -= 1
        @unknown default:
            break
        }
    }
}

As developers, we have a huge influence on whether an app will be usable by everyone, or whether it will simply be inaccessible to some users. As we've seen in the examples, often all it takes is a few tweaks to the code - properly set attributes, thoughtful navigation, or appropriate element state management - and the application becomes much more understandable to those who rely on assistive technologies. 🛠️📱

When developing, it's important to think about:

✔️ Logical navigation - elements must be read in a meaningful order

✔️ Proper labeling of components - a Screen Reader needs clear descriptions

✔️ Interactive elements - buttons, tables and forms need to have clear functionality

✔️ Grouping of elements - relevant information should be linked to make sense without visual context

Accessibility is not just a 'nice-to-have' - in many cases it is a legislative requirement and not least good software engineering. Well-written code means better scalability, sustainability and better usability for all.

So the next time you implement new functionality, ask yourself:

➡️ "Will this work for someone who can't see the screen?"

➡️ "Can a user understand the functionality using only a Screen Reader?"

➡️ "Do the navigation and interactions make sense even without the visual layout?"

If not, maybe it's time to add a few accessibility fixes. Small changes in code often make a huge difference in user experience. 🔥

💡 Source code can be found on  GitHub.

Andrej JaššoiOS Developer