|The latest version of our app is nearly ready to drop into the app stores. It's been a challenging project with more than a few hurdles along the way. The project remit initially was to add the ability for our drivers (the user's of the app) to track their journeys to allow them to make mileage expeneses claims. By logging their journeys, drivers would have evidence of their claimed mileages. At the outset this seemed like a really useful feature that should be straight-forward. Tracking a user's movements using an app is certainly nothing new, so we certainly didn't foresee the issues we would later encounter.
The current app is developed using Xamarin Forms. All functionality and business logic is supplied to the app by way of ASP.NET WebAPI RESTful services all of which are hosted on Azure. All of the services are processed by an Azure Service Bus to ensure we can scale the app and to add resiliency. The majority of the code within the current app is shared code (Xamarin Forms apps allows for the code that is the same across the different platforms to be contained in one project, whilst the Android and iOS specific code are contained in separate projects respecively).
We began the project with the aim of keeping as much of the journey logging code in the shared project to keep the platform specific code to an absolute minimum. This ambition was quickly forgotten when we got down to the details of the project. We soon realised that it wasn't possible to fully realise the journey logging functionality without writing a lot of platform specific code as so much of it was tied to the specific hardware on the devices. Although the cross-platform geolocator service we used ran on both platforms (courtesy of James Montemagno), we wanted the ability to run the tracking service as a background process.
Current devices have very strict constraints as to how they will execute long running processes (and quite rightly too). We needed to run the journey logging in the background, as modern platforms just don't support executing long running processes in the foreground. These constraints were different across the different platforms. Android and iOS handle long running processes differently, and their constraints and solutions are unsurprisingly different too.
We next wanted to enable local push notifications to keep the driver informed that the tracking service was still recording in the background. Especially if the user brought another app to the foreground, made a phone call, or in some way forced our app to the background. Local push notifications are handled completely differently on the different platforms, leading to further platform specific code. Implementing the journey logging service as a background process, and implementing local push notifications all entailed having to write vast swathes of platform specific code.
All of these platform specific deviations brought up brand new problems, and showed the many discrepencies between Android and iOS. Although Xamarin Forms does a magnificent job of hiding as much of these deviations as possible, there were many times on this project when we were fully exposed to the inner workings of the platforms and needed a deep understanding of the native APIs. iOS particularly threw up many problems. In particular, it was incredibly difficult trying to submit large journeys as a background service. On Android, it was relatively straight forward getting large uploads to run in the background. It was far more technically challenging on iOS due it's vastly more restrictive environment and permissions.
The services that support and provide all the functionality to the app are all ASP.NET WebAPI RESTful services. The services needed to support the journey logging functionality were initially thought to be straight forward. All we would need were services that would allow the journeys to be created, updated and deleted from the device. During initial testing with the app we came across several issues when trying to submit journeys from the device to our cloud hosted Azure SQL DB. Initially we ran into issues when submitting journeys from our development environment. After much head scratching and investigation I eventually pinned this down to a rule on our firewall that was set to truncate any outgoing traffic that exceeded 1MB in size. After resolving this issue, we ran into a further similar problem when we attempted to submit journeys from our staging (Azure) environment. We were getting SocketException errors. After further diagnosis we found that we could send smaller packets of data successfully. The error only appeared when attempting to send large journeys in one go. So I had to write a chunking algorithm to decompose larger journeys in multiple smaller journeys. This required making substantial changes to the underlying WebAPI service, as well as changes to the app code itself.
Another new feature of the app is the ability to create the main menu dynamically. Against each company we store a list of the menu options that will be available to them when they open the app. This gives us the ability to turn app features on and off for a company dynamically. Each time the app is launched we check the menu options dynamically at run-time. This allows us to update a driver's list of menu options without them even having to log-off or restart the app. It's all done while the app is running.
We're now in the final stages of testing the app and are hoping to have it in the stores very soon. It has been a real trial-by-fire. We encountered many problems along the way, and with much grit and determination, have managed to overcome all of them. The project has been challenging to say the least, but ultimately successful thanks to the sheer determination of the development team.
"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." - C.A.R. Hoare