Creating & managing dev, staging and live versions of your Xamarin app

Share the joy
  •  
  •  
  •  
  •  

When we started building our IOS App, in a team including developers, designers and QA it became apparent that having a single version of the app, with a single version of each dependent service, didn’t really work.

To solve this issue we created three separate versions of our app. As a result each member of the team now has access to different versions, each with their own level of expectations.  We managed this through source control and our continuous deployment system (Jenkins).

cropped

So how did we do it? In two parts: compiled configuration settings and managing the iOS info.plist file. Managing compiled configuration settings will be the more familiar solution to regular .NET developers. This involves a simple combination of dependency injection and compiler flags. Managing the info.plist is a little trickier as it uses conventions of the iOS compilation process itself.

Compiled configuration settings

Connecting to third party services is something most apps will eventually have to do. Whether it’s for analytics, crash reporting, push notifications or social sharing. Most providers will have at least Staging and Live equivalents. If not, you can create your own. For example, we have multiple Facebook Apps for staging/live for Login with Facebook functionality. The main dependency for the JustGiving app is the JustGiving Public API,

Using a combination of dependency injection and compilation symbols, we managed a distinct set of configuration settings for each of our development, staging and live app environments.

To begin we edited our configuration options of the iOS project to reflect our 3 environments:

Solution Configuration Options
Xamarin Studio – Solution Configuration Options

This allowed us to compile the app in local development version (DEBUG), a staging version (RELEASE) and a live version (APPSTORE). You can name these options whatever you like.

Next, we created a set of configuration settings that our dependency injection framework could bind to differently based on those compilation symbols.

Now, in the setup code of the app, we have the following code to bind a different concrete implementation of our ConfigurationSettings class to our interface IConfigurationSettings. This is based on the relevant compilation symbol. We use MvvmCross, and its inbuilt dependency injection framework, but the concept is the same regardless of what DI framework you use.

So that sorts the configuration settings side of things out. Now let’s delve into the info.plist.

info.plist.

The iOS info.plist file can be thought of by .NET web developers as the equivalent of web.config. All manner of iOS-relevant configuration options appear in the info.plist including version information, supported orientations and supported iOS versions. Some iOS 3rd party libraries that you include can also have requirements for values in this file, for example the Facebook SDK requires your Facebook App ID.

We originally had one info.plist file, and then our Jenkins build server would use plistbuddy [link] to change individual values at build time depending on the version being built. However this meant the values were being stored by Jenkins rather than source control with everything else. This made it hard for the developers to access and change if necessary. So we refactored to having multiple versions of the info.plist file, one for each environment:

An info.plist for every environment
An info.plist for every environment

As with the compiled configuration settings before, this now allowed us to easily manage three completely distinct build-time configuration option sets. A few of the more useful info.plist properties to differentiate between environments are:

CFBundleVersion
We have ‘Development’ or the Jenkins build number here. This appears in the Settings screen of the app, which means any team members unsure of the version on which they’re on can quickly look it up.

CFBundleIdentifier
Things can get messy if you don’t vary the BundleId, which you can think of as the main identifier of your app. Many user-facing functions depend on BundleIds being congruent across services. For example, our Azure Mobile Services staging app for push notifications is provisioned with an APNS ‘Test’ certificate generated for an app with a BundleId of ‘com.justgiving.app.staging’. This same BundleId appears in our (staging) Facebook App iOS settings as well. As iOS devices can only have a single version of a single app (as identified by the BundleId) installed at once, having a different BundleId for each environment allows users to have all 3 versions on their devices at the same time.

XSAppIconAssets
Using Asset Catalogues became the preferred way to manage and ship assets from iOS 7 onwards. The good news is that if you’re doing it this way, it easy relatively easy to vary your app icons by environments. This is very useful to those members of your team who have multiple versions of the app on their phones at any one time.
We have a separate App icon asset catalogue for each environment:

An Asset Catalog per environment
An Asset Catalog per environment

And then the relevant value in each info.plist file:

Dev environment info.plist

This results in our beautifully illustrative and differing app icons:

App per environment

The final question is, how do we use these different info.plist files? Well, Jenkins (app ‘Release’ build job) simply deletes the info.plist on disk (which is the development version) and renames the ‘Info.Release.plist’ to ‘Info.plist’ prior to compiling the app as normal. Similarly, a step in the ‘AppStore’ Jenkins job renames ‘Info.AppStore.plist’ to ‘Info.plist’.

Jenkins renames the relevant info.plist files
Jenkins renames the relevant info.plist files

Simple but effective!
Keeping separate apps for each of your environments avoids confusion when your internal users are using your app, provides solid and predictable testing for your QA team, and allows you to diagnose issues with more confidence. Using the two tricks described above, you can do this effortlessly and be on your way to further scaling up your app development.

This was originally posted over at www.markgibaud.com


Share the joy
  •  
  •  
  •  
  •  

About the author

Mark Gibaud

Lead Developer, App & API Platforms.

View all posts

2 Comments

  • Hi Victor! I have not implemented this for a Xamarin Android app, but I am guessing that you’d just do exactly the same thing as with the info.plist, ie. you would have an AndroidManifest.xml for dev, and then also include additional AndroidManifest_Release.xml and AndroidManifest_AppStore.xml files for your configuration settings for Staging/Live environments. Jenkins would rename these files at build stage as described above.

    For Xamarin.Forms, XF is just a layer on top of an existing Xamarin Classic app which has all the same compilation characteristics, so the above should work great with a XF app (although I haven’t explicitly tried it myself).

    Hope that helps!

Leave a Reply

Your email address will not be published. Required fields are marked *