Xamarin Development Setup

Part 2. Continuous Integration/Delivery

With our initial solution in place, we will now start defining our build/deployment pipelines. So let us quickly sum up what we want our pipelines to do.

First of all, we want to build the project and run our unit tests each and every time we make a commit, and before we are allowed to merge a pull request. We can do this quickly and often, as the build time is usually a measure of minutes, and so are the unit tests. Second, we might want to run UITests or integration tests on a scheduled basis. We run these less often because they are more expensive both in terms of time and financial cost. Depending on your preference and budget, nightly or weekly is likely a good choice. Running these tests on a schedule helps give confidence that the user interface and integration with the api and/or other services stays correct, and gives us timely notice if everything is broken. Lastly, we want to be able to automatically deploy our app to our internal testers, beta testers and finally our end users.

Since we are likely deploying an iOS and an Android app (we are probably using Xamarin Forms for its cross-platform convenience after all) we will need to define a pipeline for each of the platforms.

Build/unit test

This step is probably the most straightforward, and you could probably find many examples on this on other sites or blogs. For the sake of completeness a simple example is included for both iOS and Android.

UITest/Integration test

As mentioned, we want a scheduled UITest/Integration test run. The following sample defines these.

Publishing the app

The final part of the pipeline is going to make the whole more complex. We cannot reliably change settings per environment as we would on software that we host ourselves because the software runs on the users device. This means that things like backend api base urls will need to be compiled into the app. If you have multiple environments (test/acceptance/production) you will need a different build for each environment.

To make this possible, we can utilize some compiler tricks. First off, define a class with a recognizable name for enivornment-specific settings, for instance AppSettings.cs. Next, add classes for each environment. For example, I would add the following:

  • AppSettings.cs
  • AppSettings.development.cs
  • AppSettings.test.cs
  • AppSettings.acceptance.cs

AppSettings.cs will contain production settings, AppSettings.test.cs will contain settings pertaining to testing and so on. We will conditionally include these files in our solution, so that only the right file is added for each environment.

Next, we can add instructions to our build pipelines to perform builds for each of the environments. We can then use the built in ‘Publish to App Store’ and ‘Publish to Play Store’ tasks to publish our app to the right channels. For iOS, testing and acceptance will be through testflight, for Android, testing and acceptance will be Internal/Alpha/Beta test tracks. The end result will look something like the following: