One of the biggest differences between web apps and mobile apps is the decentralized nature of mobile apps. Instead of pushing updates into servers or the cloud, we instead push the updates through the App Store and Google Play Store where the updates are pulled down by our users.
The fact that updates are not guaranteed can create some work for us as developers, particularly when we need to make a breaking change to our backend. Ideally, we want to support backwards compatibility where we can, but it’s not always possible or realistic.
In order to avoid frustration, a flurry of support calls and having to react to the situation, you can use a simple force update flow to inform your users that their app needs to be updated.
Before we get to the code, let’s break this down into the steps of the force update flow:
- The app sends a request which includes the app version to the API.
- The API processes the request, comparing the app’s version from the request to a minimum version defined in the API configuration.
- If the app’s version is less than the minimum required version, the server responds with 400 Bad Request and some information about the API version and minimum client version.
- The app handles the response and navigates to a modal page with more information.
You’re likely tired of my rambling, let’s get to the code!
Send The App Version to the API
We’re going to extend DelegatingHandler which we will pass to our HttpClient constructor, this allows us to intercept requests/responses and do some processing. Incidentally, this is also what HttpTracer does.
Pretty straightforward, we override SendAsync and intercept the request in our ProcessRequest method. This naming convention is borrowed from MessageProcessingHandler, another BCL class, I wasn’t able to user MessageProcessingHandler because we need our processing methods to be async. Now, I know what you’re thinking, “Dylan’s an idiot, he’s not even doing any asynchronous processing”, this is true, I’m not, but I do need it to get the response content and I want the method signatures to be consistent.
Our request is augmented to include a “client-version” header, this is defined in a common library where we keep a few pieces of code which are used in the API and the client. The version value is pulled from Xamarin.Essentials.VersionTracking. The version value is the same as you would see on the stores.
Process the Request on the API
There’s a few moving parts with this. Let’s look at this from the bottom of our dependency chain up and we’ll address registration last.
We all love our interfaces, we start by defining IVersionCheckService with two simple members.
- PerformVersionCheck – this looks at the request headers, if we have a “client-version” header specified and it’s less than the MinimumClientVersion defined in our config, we throw a ClientVersionNotSupportedException.
- MinimumClientVersion – this exposes the configuration value for usage elsewhere if required.
Now we call our VersionCheckService from an implementation of IActionFilter, this makes it simple to decorate our controllers to specify where we’re going to enforce the version check. Note that we’re using ASP.NET Core’s build in DI which means our attribute definition looks slightly different.
Process the Response on the API
Here’s our ClientVersionNotSupportedException and our Startup registration.
There’s a bit of magic to the serializing in ClientVersionNotSupportedException to make sure we can get the information out that we need.
We’re doing a few things in Startup.cs:
- Lines 34-36: we’re registering our configuration, service and filter.
- Lines 42-46: we setup a custom exception handler
- Lines: 62-71: we handle our ClientVersionNotSupportedException
Process the Response on the Client
Back to our VersionCheckHandler to handle the exception in the response.
This is our final handler, see how I have added the ProcessResponse method which takes care of deserializing our ClientVersionNotSupportedException and throwing it.
Finally, we catch the ClientVersionNotSupportedException on lines 42 & 65 and navigate to a modal page informing the user that they need to update.
Blogging is hard. This took a lot of time and effort, the write up started with a longer version which accounts for offline scenarios. I’ll get to that later, this is enough to digest for now.
I really hope someone finds this useful! Please let me know if you have any feedback!
The code is here if you would like to run it up: Xamarin App Version Checking