IOS

Accessibility in UK retail mobile apps

Oliver Brown
— This upcoming video may not be available to view yet.

I have slowly been getting more interested in the accessibility of mobile apps. This has partly been driven by finding out just how many options there are to improve accessibility of apps, both for users and developers, and realising that many developers don’t know they exist. And, even when they are known about, there isn’t always much information about how to use them well.

To make this a bit more robust, I installed a bunch of UK shopping apps just to see what the current state of accessibility was and the results were… disappointing.

In fact, the issues I found were bad enough and widespread enough that I decided to write a series of posts going over some of the issues and explaining methods to improve them.

Testing details

The following is based on an iPhone 15 running iOS 18.5. Despite being an Android user myself, I have far more experience developing for iOS. I want to approach this with the goal of improving things and not just dunking on apps with shortcomings. For iOS I can give more concrete advice about how to make things better.

I also limited myself to just browsing around the app. I did not create an account, I did not attempt to purchase.

Summary of common issues

Before I get into the weeds of specific problems in specific apps (and hopefully how to improve them), I have a summary of common issues that were present in most apps.

Lack of large text support

Users can change the size of the text on their device, and many do. In fact this is probably the most used accessibility feature with somewhere between 20% and 30% of users selecting a larger text size.

For iOS developers, this feature is called dynamic type. For many system controls it is enabled automatically.

For custom controls, it is is enabled automatically in SwiftUI (but can be disabled), but has to be explicitly enabled in UIKit.

Cross platform frameworks vary. Xamarin did not support it and getting to work was tricky. MAUI supports it by default.

For the apps I tried:

  • Several had no support at all for large text. They are likely using UIKit for everything, or a wrapped web view.
  • Most had sporadic support for large text. The specific places suggest it was accidental. This could be some system controls without a custom font, or SwiftUI controls embedded in UIKit.
  • A few had broken support - some screens mostly unusable. Likely using SwiftUI with automatic support but with no testing.

Voiceover

iOS has a built-in screen reader, VoiceOver. Standard controls are well supported automatically, and simple layouts should just work. Any even slightly complex app will require additional work to make sure things have the correct labels, ordering is sensible, and element grouping makes sense.

Sheets and popups

Several apps had problems with modal displays that were not actually modal. That is, a full screen popup that still let you interact with UI in the background (that you couldn’t even see).

Worst of all, most of these examples were during the app onboarding making it the first thing a user would interact with.

Most also had smaller popups or menus that didn’t focus properly when they appeared.

Ordering

A key feature of VoiceOver is sequential navigation. You can always navigate to “next” and “previous” elements under the assumption that user may not be able to see the screen at all. For this to work the content has to be sensible order.

Simple cases will just work, but grid layouts may not work automatically, and overlapping elements will almost certainly have some undesirable ordering.

Every app had at least one screen that handled this very badly. They also had screens that were just about usable but not pleasant.

Grouping

By default every element that has text or is interactive will be treated as a separate element to VoiceOver. This isn’t always desirable.

One common example is multiple text elements (often with different formatting) intended to be read in one go are split up. Every app had at least one example of this, with some handling badly in many places.

Another one that is a bit more subjective is handling long lists. Imagine a list (or commonly a grid) of products with a title, a price, a brand name, a star rating, a “like” button, and an image. If each of those is an individual element, it takes very many “next” swipes to get through items. Nearly every app had this problem.

Labels

Text content will be labeled automatically, but images need to have labels added manually. If one isn’t specified, iOS will often read out the file name of the image which is often nonsensical.

Every app had at least one example of this. Most also had at least one label that obviously added but not useful (for example an image just labeled “Promotional banner”).

Other interaction methods

As well as VoiceOver, there are a few more ways to interact with an app that largely depend on the same works as above - good naming, ordering, and grouping.

Switch control

With switch control interaction can be limited to a small number of switches. This is not something I have tried very much, but it fails without good item ordering.

Voice control

Select elements by saying “Tap X”. Unintuitively this is quite resilient to poor element naming since you can see* the labels and it has built-in support for disambiguation. What does cause problems is insufficient grouping. Most of the apps had areas with too many individual items in close proximity that were hard to select.

* I used it while I could see. This can be combined with VoiceOver by a user who cannot see. In which case all the difficulties of VoiceOver also apply.

Other minor points

Invert

iOS has a smart invert option that inverts all the colors. Developers should annotate photographic imagery to be not inverted, but most of the apps did not.

Bold

Users can choose to make all text bold. This will be supported automatically if using system fonts, but not if using custom fonts. None of the apps supported this properly, with only a few controls (likely using system fonts) appearing bold.

Accessing the UI in Xamarin.iOS

Oliver Brown
— This upcoming video may not be available to view yet.

An interesting discussion happened recently, triggered by a code review of (something like) the following C#:

public class ProductViewController
{
    private UIView _detailsView { get; set; }
    private ProductViewModel _viewModel { get; set; }

    private void Process()
    {
        if (_viewModel.ProcessDetails)
        {
            InvokeOnMainThread(() =>
            {
                if (_detailsView != null)
                {
                    UpdateDetailsView();
                }
            });
        }
    }
}

One of the reviewers suggested moving the second if outside the call to InvokeOnMainThread (and then combining it with the other if).

The theory was that it would more efficient to check if we need to be on the main thread before we do it, instead of doing the check on the main thread and finding out we have nothing to do.

The original author pushed back saying you can’t access _detailsView off the main thread since you would get an exception.

On the surface this sounds reasonable - everyone knows you can only access UIKit objects on the main thread. But it naturally leads to the question: what does “accessing a UIKit object” actually mean?

So I wrote a quick sample that intentionally tries to do lots of UIKit manipulation on background threads in various ways:

The tests

Quick refresher. The View property of a newly constructed UIViewController is not populated until it is first accessed. Some of the tests below refer to accessing this property either before or after it is created. By default properties declared in C# will not be visible to any native iOS code. They can be made visible by adding the [Export] attribute.

  • Create a UIViewController.
  • Create a UIView.
  • Create a UIColor.
  • Check if a view controller’s View property is equal to null.
  • Check if a view controller’s View property is null using pattern matching.
  • Check if a view controller’s IsViewLoaded property is equal to true.
  • Check if a view controller’s View property is equal to null after previously creating View on the main thread.
  • Check if a view controller’s View property is null using pattern matching after previously creating View on the main thread.
  • Check if a view controller’s IsViewLoaded property is equal to true after previously creating View on the main thread.
  • Check if a new UIView property, that is not exported is equal to null.
  • Check if a new UIView property, that is not exported is null using pattern matching.
  • Check if a new UIView property, that is exported is equal to null.
  • Check if a new UIView property, that is exported is null using pattern matching.
  • Check if a view controller’s NavigationController property is equal to null.
  • Check if a view controller’s NavigationController property is null using pattern matching.
  • Set a new UIView property, that is not exported, with a view created on the main thread.
  • Set a new UIView property, that is exported, with a view created on the main thread.

Results

Test Result
Create UIViewController Exception
Create UIView Exception
Create UIColor OK
Check if View is equal to null Exception
Check if View is null pattern Exception
Check if View is loaded Exception
Check if View is equal to null after creating View Exception
Check if View is null pattern after creating View Exception
Check if View is loaded after creating View Exception
Check if non-exported view is equal to null OK
Check if non-exported view is null pattern OK
Check if exported view is equal to null OK
Check if exported view is null pattern OK
Check if NavigationController is equal to null Exception
Check if NavigationController is null pattern Exception
Set non-exported view OK
Set exported view OK

Summary

This is by no means exhaustive, but it seems in general, acessing properties declared natively in iOS will throw an exception whereas accessing properties you have declared yourself will be fine. I had a suspicion that an exported view might behave the same as a native property, but apparently not.

The code for this test is available on GitHub.

The future of Microsoft MAUI (and Xamarin Forms)

Oliver Brown
— This upcoming video may not be available to view yet.
Xamarin
Xamarin

Since Google seems to like my post about the future of Xamarin Forms so much (and I have a slight history of such posts), I’d figure I’d post an update about interesting things happening in the Xamarin Forms repo specifically related to MAUI.

Renaming

The change that actually made me write this post - a large PR with 5000+ changed files that changes the Xamarin Forms name to MAUI.

Not much of a thing for actual functionality, but a significant symbolic milestone.

Handlers and the great big architecture shift

.NET MAUI will completely change the way renderers are handled in Xamarin Forms. There are many advantages of doing it the new way, but the mechanics of how it is done are fairly complex. This video by Javier Suárez covers it well.

Interacting with this video is done so under the Terms of Service of YouTube
View this video directly on YouTube

This is all happening right now in the main-handler branch.

Update

While I was writing this, work officially moved to the dotnet/maui repo and it is accepting pull requests directly.

AppHost and Microsoft.Extensions

Originally an ASP.NET concept, that then migrated its way to Windows client development, this provides a common way to provide dependency injection, logging, and lots of other infrastructure stuff. In isolation, the pattern and implementation is good and will make it easier to override certain things in MAUI (such as handlers). It’s also useful in a wider sense since it will make configuring different styles of .NET apps more similar.

Single project

Over the past couple of years there has been a move towards producing Xamarin libraries (and .NET libraries in general) using a single multi-targeted project. The most significant is probably Xamarin Essentials. This PR adds support for creating applications following the same pattern.

Merging in Xamarin Essentials

There is a lot of functionality in Xamarin Essentials that Xamarin Forms would like to use. Likewise there is some functionality in Forms that is useful when not using Forms. This lead to some overlap in functionality (and occasionally overlap in APIs but not a perfect match in functionality).

There was an attempt to add Essentials as a dependency of Forms but it faced some problems, and there was a “change of plans”.

Now the solution is to have Forms and Essentials in the same repo. I hope Essentials remains available as its own Nuget package (and it looks like that will be the case).

Resizetizer.NT

Resizertizer.NT, like its predecessor Resizetizer, is a package for generating platform specific images in all the right sizes at build time.

Managing image assets across iOS and Android (and using Visual Studio) has always been an unpleasant process. This tool makes it much easier and will be included in MAUI by default.

The future of Xamarin Forms

Oliver Brown
— This upcoming video may not be available to view yet.

Microsoft have just announced the future of Xamarin and Xamarin forms - the .NET Multi-platform App UI (or MAUI for short). As a name, it’s not great. The highlights are as follows:

  • It’s an evolution of Xamarin Forms. It basically is Xamarin Forms, but finally accepting some breaking changes. To be honest, I’m hoping for a lot since there is a lot of weirdness in Xamarin Forms that has been holding it back.
  • Single project, multi-targeted. It took a long time to get to the point where this was possible. From shared projects, to PCL projects, through .NET Standard. This should make things a lot easier.
  • Still based on platform renderers using native controls. This is a mixed bag. Using native controls has long been a selling point of Xamarin (with or without Forms). With the rise of Flutter this has been shown to be less important. Many people have been asking for consistent platform agnostic renderers instead.
  • The end of “Xamarin” as a name. Some time in the .NET 6 timeline (end of 2021) Xamarin.iOS will become .NET for iOS and Xamarin.Android will be .NET for Android. I have mixed feeling about this since this was a fairly succinct way to describe by top skillset.

I also already have my own enhancement issue submitted.

Accepting money on the iOS App Store - acquiring an EIN

Oliver Brown
— This upcoming video may not be available to view yet.

To sell an app (or an in-app purchase) on the iOS App Store as a non-US citizen, a tax ID is required* to fill in a W8 form. That is either an ITIN (Individual Taxpayer Identification Number) or EIN (Employer Identification number).

As an individual, the ITIN would appear the most correct option. The last time I investigated it, it was a slow process that would require notarised copies of supporting documentation.

Getting an EIN is much easier, and despite the name, does not require you to have employees. All you need to do is be in business (which selling in the app store is) and being a sole proprietor (sole trader in the UK) counts.

In theory, you fill in form SS-4 to get one. It’s very straightforward, and even has an explanation of exactly which parts you need to fill in (look for notes for “IF the applicant… Is a foreign person needing an EIN to comply with IRS withholding regulations”.

In practice, it is even easier than that. You can apply for one over the phone. They will basically ask you the same questions as on the form, and then give you the EIN there and then. I called “first thing in the morning” (6am for them, 11am for me) and got an answer immediately (they warn you that you could be waiting an hour during busier times). The whole process took less than 10 minutes. The hardest part was confirming how my address should be written down. The net result of all this is that Tic-tac-toe Collection’s single in-app purchase has been enabled (at least for beta testers).

* Whether it is actually required is unclear. The old Xbox 360 program, Xbox Live Indie Games, allowed developers to sell without a tax ID, but a 30% withholding tax would be deducted from your earnings. I believe the best result would be the same happening here. It’s also possible Apple would just not allow you to process sales at all.

ListView improvements in Xamarin Forms

Oliver Brown
— This upcoming video may not be available to view yet.

Since becoming open source, it has become possible to find out potential upcoming features in Xamarin Forms by just poking around the active branches in the repository (macOS support was visible in the repo before any announcement).

One of them is lv2spike. From just reading the commit messages, it seems this is a new CollectionView, based on UICollectionView for iOS and RecyclerView for Android. This is something that has been needed for a while, but is a big enough undertaking that I understand why it has taken a while. After all the branch suggests this is still just a spike. There are quite a lot of feature requests for the Xamarin Forms ListView that are just not possible (like this one for horizontal layout) mainly because the iOS implementation is based on UITableView.

This will open lots of possibilities. My biggest concern is that despite the push forward with features, Xamarin Forms is accruing bugs even faster, and with the expanded platform support this could just get worse.

[SOLVED] System.ExecutionEngineException: Attempting to JIT compile method

Oliver Brown
— This upcoming video may not be available to view yet.

TLDR: Check multiple references to the same nuget package are all on the same version if you use the Mono linker.

Since my ability to post regularly on things I’m interested in is not great, I figured I could at least post stuff that might be useful.

I recently upgraded a Xamarin iOS app from the “classic” (32bit only) API to the Unified API. After doing so I got the error message:

System.ExecutionEngineException: Attempting to JIT compile method
```.

This is caused by the Xamarin (Mono) linker removing code that is only referenced dynamically. The usual solution is to let the compiler know somehow that you are using the code (using a Preserve attribute if it's your own code or something like MvvmCross's [LinkerPleaseInclude.cs](https://github.com/MvvmCross/MvvmCross/blob/f72a92e8a81b9179d1f75d6214eee8c9ca176221/nuspec/TouchContent/LinkerPleaseInclude.cs.pp) otherwisr).

In my case, this did not fix the problem. It turns out the Unified API upgrade was a red herring. I had also updated a few nuget packages at the same time. One of them was used in several projects, but I'd missed updating one of them (so I had Project A using v1 of a package and Project B using v2 of a package). This meant my efforts to stop the linker from removing some stuff only worked on one version of the package.