Title Image showing a factory

Azure DevOps, formerly known as Visual Studio Team Services or VSTS for short, allows you to create automated release pipelines for all different kind of projects. One of the nice things is that you get free build time for opensource projects. So why not give it a spin and look if I can set up the build pipeline for my open source project PureLayout.Net. The PureLayout.Net library is a wrapper of the PureLayout iOS library written in Objective-C which allows you to quickly layout your UI in code. So it differs a bit from your standard Xamarin project as it involves the step of building the project, creating the bindings to C# and then packaging all up in a NuGet package. Since this is an iOS-only project, we will, of course, have to build this on a Mac.

Choosing a build agent

Good thing then that you Azure DevOps (could we all agree on ADO for this in the future? ) provides a Mac build agent hosted on Azure. Now the question left is, will the hosted Mac provide all the tooling that we need? If no, we would have to fall back onto the option of creating our own Mac build agent, i.e. renting it from a third party. For PureLayout.Net we require the following tools to be installed:

Luckily on GitHub the agents OS and tools are all listed. So we see that there is the Xamarin Toolchain and XCode. Unfortunately, objective-sharpie is not installed and while this is a bit of set back what we see installed on the hosted macOS agent is homebrew.


Homebrew is THE package manager for macOS, and it allows opensource projects to provide their tools as packages. A quick search on the interwebs shows there is a keg for objective-sharpie - yes they are going all the way on that brewing analogy. So we could install the tool while before we run the build. So let's go ahead, and set up the build with the hosted macOS agent from ADO.

Hosted vs setting up your Build Agent: There are a few things to consider when choosing between setting up an agent on your own and then register it to ADO or opting for a hosted build agent. While it always depends on your situation. Generally speaking, setting up your build agent brings you more control over the setup and installed tools. You decide when updates happen and can give you have the option of having databases/files and so forth pre-setup and ready for reuse. On the other hand, you must maintain your agent and while this might work okay at first. Consider having to maintain multiple agents. How do you ensure that the agents are all equally setup? MInor differences in the setup could lead to an unstable build infrastructure which is something no one wants. If you do not have any requirement that prevents you from using hosted agents, I would recommend going the easy route and using hosted agents. Hosted agents come with the added benefit of being able to adjust the number of agents according to your workload - if you are a consultancy, this can be a huge bonus since project/build load might vary from time to time.

Configuring the build

The browser is all you need for setting up your build configurations or pipelines as ADO calls them these days. While ADO does offer templates for specific builds such as Xamarin.Android, there is no template for Xamarin.iOS wrapper projects - other than the blank template that is. The first step is to connect the repository which in case of PureLayout.Net is on GitHub. For the first time, one has to link GitHub with the ADO account by following the instructions.

When building PureLayout.Net manually. The the following steps create a new version of PureLayout.Net:

  1. Execute make
  2. Build the solution
  3. Pack the artefact into a NuGet package
  4. Enjoy the new NuGet package

The Makefile creates the native binary (and generate the required wrapping code). To create the wrapper, we require objective sharpie which we can install via Homebrew. To execute all the required commands in ADO a Command Line build step with the following instructions is used:

echo install objective sharpie
brew cask install objectivesharpie
echo Performing make

If you ever want to go down this rabbit hole of creating your wrapper project. Be sure to check out this article by Sam Debruyn - after reading this post of course .

Next up is building the solution of the wrapper project, which can be done with an MSBuild step and defining the path to the csproj file: PureLayout.Binding/PureLayout.Binding/PureLayout.Net.csproj - there is even a handy repo browser. Under Configuration, you can set the build configuration to Release, but instead of hard coding it, consider using the environment variable $(BuildConfiguration). More about environment variables in a bit.


Next up is packing the compiled output into a NuGet package. By adding a NuGet build step, setting the Command to pack, the path to the csproj file and the output folder to the ADO environment variable $(Build.ArtifactStagingDirectory).

The final step is to publish the artefacts, i.e. the NuGet package. There is again a standard build step to use here called Publish Build Artifacts.

Are you still wondering about those environment variables above and how they come together? Environment variables are a great way to reduce duplicate hard written configurations in build steps. There are two kinds of environment variables those that you can define yourself and those that are predefined.

For creation purposes or if you are exploring what ADO has to offer the web UI is a great choice. However, if you talk with the grown-up DevOps engineers, they usually voice some concern over maintainability or the lack of sharing configurable definitions. One of the ways to overcome these issues is to define the entire build steps in a YAML file.

Configuring the build with YAML

YAML Ain't Markup Language or YAML for short is the file format supported by ADO to store the build configuration alongside your code. While versioning also is done by ADO whenever you change the build steps, in the UI. YAML definitions further allow you to create templates for other similar projects - which can be real time savers.

When selecting the build agent, we can view the YAML generated out of the build steps defined via the web UI.


In the projects root folder, we can now create a file, e.g. builddefinition.yaml which we can then check in to Git. Once the Git Repo contains the YAML build configuration, we can create a new pipeline based on the build config. Unfortunately, there is currently no way to use the visual designer and YAML configuration in the same pipeline.

Though not automatically exported the build trigger can also be set in the YAML file. According to the docs - which include some inspiring samples this is the resulting block for PureLayout.Net:

  batch: true
    - master
    - README.md
    - master
    - README.md

The above configuration ensures that all pushes to master are triggering a build. The pr section is in regards to Pull Requests. Note the neat trick you can do with paths which allows you to prevent triggering a build should only the README.md or similar change.

Lesson learnt: Any configuration changes regarding git you still have to do in the visual designer. In the case of PureLayout.Net, it was ensuring that git submodules are checked out.

Is YAML better than the visual ADO web-based editor? Well, it depends. If you are just getting started with setting up automated pipelines, the visual editor allows for faster results. It also provides a better experience when discovering what is available on ADO. However, if you are looking for a way to share configurations, store your build definition alongside your code and know what you want to get done. YAML probably is the better solution for you.

You can see the full YAML configuration for PureLayout.Net on here.


When I initially set out to automate the build for PureLayout.Net, it was because I wanted to reduce the hassle for me to ensure that commits or PRs would not break anything. With the steps above the manual steps left are testing the NuGet package, adjust some metadata if needed and then deploy it to NuGet.org. Since it is a visual library, I feel comfortable manually "testing" the result - which means as much as looking at a screen and checking the layout with my own eyes. The manual release steps are also totally fine with me.

Nevertheless, you could automate a lot more. For instance, push the artefacts automatically to Azure Artifacts or other places, adjust the release metadata pending on various parameters or setting the Version number on the fly. Another possible area to automate would be the testing side and and and... For me I left it here, well not entirely, I still published the build status to GitHub - and this blog :

Build Status



I recently upgraded my developer machine from a Surface Pro 3 to a Surface Book 2. I have been using it now for a good month and wanted to write down my experiences, and how it holds up to the daily tasks, I throw at it as a mainly mobile developer. That is at the office and during leisure time.

Disclaimer: I did not receive any money from Microsoft for writing this article nor was I asked by them to do so. I am doing this out of the sole purpose that at the time when I was looking for a new dev device I could not find an article on the SB2 for the mobile dev.

Now when looking for a new computer, it always depends on what field you are active in. In my role as (mobile) developer, team lead, occasional speaker and human being I tend to use my device in multiple different ways. Further, I always fancied the idea of owning one device that can handle all kind of different workloads, so while the Surface Book might not be the ideal tablet, it can be a tablet which should eliminate the need for owning an additional tablet, e.g. an iPad or the new Surface Go. But more on that later.

The version I am using is the 13.5" model sporting the i7, 16 GB RAM, 512 GB SSD and an NVIDIA GeForce GTX 1050. While not on the cheap side it comes with the marketing statement of being the ultimate laptop.

Overall impressions

The 13.5" inch model comes with a beautiful screen. Having a resolution of 3000 x 2000 (267 PPI) is great and with a bright display which means you can also work outside if you want to. What I like is the 3:2 screen resolution which is great for developing, writing and being productive.

The keyboard is excellent to type on. It has a nice feel, the spacing of the keys is nice, and the travel of the keys is nice, and I do not get the stares as some MacBook Pro owners get for their clackidiclack keyboards. Only thing bothering me here is the background lighting of the keys. This is my biggest personal dislike of the machine. For one the keys are unevenly light, which is just odd for a device at this price. Further, it has no automatic sensor for turning it on or off. You can set it manually to 3 brightness levels (or off). So far okay I can live with that, but what I found irritating was that in some lighting environment it was quite hard to read the keys with the background lighting on as they seemed to start matching the magnesium-silver colour of the keys. I never had this issue with other laptops so far, but that might be because I only had black keys so far? Anyway, if the keyboard backlighting is the worst thing on your machine, you know you are starting to get old I guess

The trackpad is okay. It is a glass trackpad and allows you to get where you want to on the screen. Overall I still do think that the MacBook Pro has a better trackpad, but I never feel the need to get a mouse when working with the machine in laptop mode. So nothing to worry there.

Coming with a 16-hour battery life and coming with dedicated GPU power does make the Surface Book 2 a touch heavier than other devices at 1.642 kilograms (for you imperialists that's 3.62 lbs). Especially compared to a Surface Pro. But the 13.5" is still a portable machine which you can carry around in your bag and coming with a large battery it does mean you can leave that bulky charger at home. The charger does come with a handy USB-A port which allows you to charge your phone/smart-watch/kindle etc. without requiring additional chargers to be brought along.


Using it as my daily development machine

Being a 2-in-1 device is great, but at the end of the day, I primarily will use it as a laptop and dev machine. During my day job at Noser Engineering, I work mainly on mobile solutions (mostly Front-End) and other projects in the .Net space. So the daily load usually involves Visual Studio, some Browser (Firefox or Edge), PowerShell (doing Git via UI just feels wrong - that is after forcing your brain to learn the commands ), Outlook, Teams and last but not least Spotify or Groove Music (second when I feel in the mood for some Music to Code By).

I use the Android Emulator quite a lot when working on mobile projects. The emulator tends to use quite a bit of RAM, and on my old device, I was often struggling to get decent performance. But on the Surface Book 2 all is well. Coming with two USB-A Ports means I do not have to worry when plugging in any dongle or accessory such as LAN-Adapter, Presenter stick etc..

Having a touchscreen is a bonus when using emulators/simulators as you can use the app as intended with your finger. Plus making gestures with a mouse is cumbersome.

In some projects, I also have to fire up some VM for various tasks. While the 16 GB of RAM holds up to a certain point, I know of many backend developers and C++ developers that need more. So keep that in mind since you can't upgrade any of the hardware of the Surface Book 2, at least iFixit convinced me with this clip.

Having a dedicated GPU is a great thing if you are into games or Machine Learning. Since I started to play around with machine learning, this allows me to play around with ML on the go. Which is nice and something a Surface Pro does not offer.

Being at your desk

While I do have to travel from time to time, I also do quite some work at an office. At work, I have a 27" and a 24" Dell monitor (daisy chained) and only have to connect the dock to my surface. The dock works excellent with the Surface Book 2. I also have used it with a Dell docking station over the USB-C port which also worked just as expected. But since I already have a Surface Dock, I rarely use any USB-C dock, which would also mean slower charging time.

I do not experience any problems with the displays (used to have a weird blackout with my Surface Pro 3 at times on the monitors), and I can wholeheartedly recommend it as being a productive setup.

Doing the meeting kabuki

No office life would be complete without the occasional meeting, right? So how does the Surface Book 2 fare when it comes to this office discipline? Really well, the keyboard is excellent for taking notes, if you decide to add a surface pen you will be able to doodle erm take notes in meetings. And have I mentioned Face ID yet? Face ID lets you unlock your Surface Book 2 with your face - bet you did not see that one coming! No, but seriously the Face ID works pretty fast with a delay of 1-2 seconds. So when my device nods off during a meeting after a couple of minutes of inactivity, I used to have to type in my Password. The delay added took so long that I gave up taking notes with my surface pen. But with Face ID it almost feels like the instant-on with a tablet. Which makes the pen and OneNote the ideal combo for taking notes during a meeting.

These days a lot of meetings are remote - period. I am a huge proponent of using the camera during conf-calls since non-verbal communication is a thing. I was delighted to see that the Surface Book 2 sports a 5 MP front facing camera which is capable of streaming 1080p HD video.

Hooking up the Surface Book 2 to an external monitor works great. You will most likely require an adapter I use a USB-C to VGA, HDMI and DVI which should solve most problems - fingers crossed. The only thing to keep in mind is the 3:2 aspect ratio. Since most presenters and Monitors are 16:9 you will have a lot of blacked out space when duplicating your display. Extending solves the problem but might be an issue if you are facing the people and not the secondary screen. I can live just fine with black bars.

On the Surface Pro 3, the fan was always on when connected to a second monitor. As soon as having the camera on and sharing the screen you could listen to the fan going from silent to ready for takeoff. The Surface Book 2 stays nice and quiet, that is if you are not training any neural net. In that case, you will get that what-are-you-doing-again glance from your spouse

On the go

The Surface Book 2 has been a great travel companion. I tend to travel mostly by train and very rarely by plane. In either case, I like to use the idle time to get some stuff done. Including writing emails, blogs, presentations and often also code. The Surface Book 2 has never let me down on any of those tasks. The only thing to remember is that it might be a bit heavier than other laptops. But for me, this is not an issue. And the fulcrum hinge allows to carry it comfortably in your hand.

The 16-hour battery life means you will stay productive for at least half a day, even when doing some heavy workloads.


During downtimes, I tend to use the Surface Book 2 more in tablet mode or in the view mode which is excellent for streaming a movie while doing some household chores. The tablet is light but is still sporting you with everything apart from the NVIDIA GPU, so you are holding a full blown pc in your hands. The battery in the tablet is not as big as the one in the base. You can, however, connect the tablet part directly to a charger/dock connector if you want to.


You quickly notice that there are not many well-designed apps on Windows 10 for tablet use. There are good (PDF) readers, Netflix, OneNote etc. but compared to iOS and Android there is still space for some well thought out and implemented apps. Which is a shame since it would make a great tablet and with Face ID it comes close to the instant feel.

Bottom line

I am happy with the Surface Book 2. It get's all my work stuff done and works well enough as a tablet that I do not feel the need of getting another device for couch surfing and watching a series.

I would recommend it to friends and repurchase it. The most significant negative point from my end is the backlighting of the keyboard and price tag. Then again you do get great value for your money in my opinion.


Title Image showing a library

When it comes to file handling and Xamarin Forms you can find all you need in the official Documentation. However, when it comes to where the data should be stored the documentation leaves some points open. Moreover, might even lead to, dear I say it, your rejection in the App Store...

Also when writing Cross-Platform Code, with .Net Standard, it does depend on the Operating System (OS) that the app is being executed on, where to store your data. So let's dive into the platforms which are primarily supported by Xamarin Forms.


The most comfortable way is using Xamarin.Essentials. Currently still in preview and requires Android 7.1 or higher. However, if that does not make you blink, these are the options:

  • Local Storage which is also backed up.
  • Cache Storage which is, well for caching files but are more on the non-permanent side.
  • Files bundled with the app, i.e. read-only files.

You can access the folder paths as follows:

var rootDirectory = FileSystem.AppDataDirectory;

Currently Xamarin.Essentials supports Android, iOS and UWP. So if all the platforms your app requires are named, and you do not need any other location. Be sure to check out Xamarin.Essentials and check the documentation for more details.

Do it yourself

In most cases the folders offered by Xamarin.Essentials will suffice, but if you require a different folder, i.e. the document folder under iOS, you can always set the path to the location on your own. So let's have a look at how you can achieve this.


Now when it comes to Android, you can follow the documentation from Microsoft and use the following path for storing your files:

_rootDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

Looking to cache some files, then we can take the path and with Path.Combine set the path to the cache folder:

_cacheDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "..", "cache");

If you want to store files to the SD-Card, i.e. external storage under Android. You have to pass in the path from a platform Project to the .Net Standard project or use multitargeting to achieve this. You can get the path as follows:

var sdCardPath = Environment.ExternalStorageDirectory.AbsolutePath;

Note that the Environment here is Android.OS.Environment and not System.Environment. So if Android is this easy how hard can iOS be?


When storing files under iOS, there is a bit more documentation to read. The reason being that Apple uses multiple subfolders within the Sandbox container. The ones that are important for file storage are the following:

  • Documents: In this folder, only user-created files should be stored. No application data, which includes that JSON file of your app, should be stored here. This folder is backed up automatically.

  • Library: The ideal spot for any application data you do not want the user should have direct access to. This folder is backed up automatically.

    • Library/Preferences: A subdirectory which you should not directly access. Better use the Xamarin.Essentials library for storing any key/value data. This data is backed up automatically.
    • Library/Caches: This is an excellent place to store data which can easily be re-created. This data is not backed up.
  • tmp: Good for temporary files, which you should delete when no longer used.

For a more detailed listing check out the docs. So when storing files, you are under iOS this code will point to the Documents folder:

_rootDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

If you intend to store information into the Library folder you can change the directory as follows:

_rootDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "..", "Library");

Equally, you can change your path to one of the other locations described above.


UWP apps usually also store their data in a sandbox. Only if in the app's metadata the permission is set and a good reason was given, which Microsofts validates on submission to the store, can the app access other file locations outside of the sandbox. UWP apps also live in a sandbox. The ApplicationData offers to store data locally, roaming or in a temporary location. The local folder can be accessed as follows:

_rootDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);

Similar the other locations can be accessed for example the roaming folder (which is synced automatically across all of your different devices):

_rootDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "..", "RoamingState");

The following directories are present in a UWP apps sandbox:

UWP Container folders: AC, AppData, LocalCache, LocalState, RoamingState, Settings, SystemAppData, TempState

However, what if I am targeting other platforms?

Since Xamarin Forms is no longer limited to Android, iOS and UWP, you might find yourself wanting to write files on another system such as Tizen. The best solution is to check the documentation of the given platform where data should be stored and then run the following code on the platform:

class Gnabber 
    // ... 
    private IEnumerable<DirectoryDesc> DirectoryDescriptions() 
        var specialFolders = Enum.GetValues(typeof(Environment.SpecialFolder)).Cast<Environment.SpecialFolder>(); 
        return specialFolders.Select(s => new DirectoryDesc(s.ToString(), Environment.GetFolderPath(s))).Where(d => !string.IsNullOrEmpty(d.Path)); 
    // ... 
class DirectoryDesc 
    public DirectoryDesc(string key, string path) 
        Key = key; 
        Path = path == null 
            ? "" 
            : string.Join(System.IO.Path.DirectorySeparatorChar.ToString(), path.Split(System.IO.Path.DirectorySeparatorChar).Select(s => s.Length > 18 ? s.Substring(0, 5) + "..." + s.Substring(s.Length - 8, 8) : s)); 
    public string Key { get; set; } 
    public string Path { get; set; } 

The code above lists all used folders from the System.Environment.SpecialFolder for the given environment, and also provides you with the absolute path. If none of the special folders is the target location, you desired. Try using Path.Combine and a path nearby to get to your desired location.


Storing files is not tricky but put some thought into where to store your applications data can go a long way. Xamarin.Essentials may provide all the functionality you need, but if not you usually use the System.Environment.SpecialFolders and the System.Environment.GetFolderPath to get access to different folders offered by the platform you are on.


Image showing a laptop with graphs on it -looking fancy that's all...

Did you know that with Visual Studio 2017 there was an update in the target project files of your Xamarin Projects? They no longer contain a packages.config file but contain the NuGet references directly in the csproj. Using NuGet references instead of the packages.config file has numerous benefits ranging from performance improvements to only showing your top-level dependencies (no longer will you have a scajilion package references ).

They also fix a pesky bug I have experienced since partially migrating to .Net Standard. Migrating to .Net Standard is not something new in the Xamarin World. There are many blog posts out there which will guide you through how to migrate your Portable Class Libraries (PCLs) to .Net Standard. My personal favorite is the approach I first read on James Montemagnos blog, which is pretty straightforward and will allow you to keep your version history.

But depending on when you have created your project you will start running into compile errors after the migration telling you that a dll from a NuGet which you have only referenced in the .Net Standard project(s) can not be found in the output folder. As is the case in our sample app which I have created for this blog post:

Showing compile error that ReactiveUI dll was not found in app output folder

In the sample project, we are referencing the ReactiveUI NuGet only in our .Net Standard code and therefore do not have a direct NuGet reference in our platform projects. When looking closely at the platform projects we can see that the Android and iOS project are using a packages.config file to reference their NuGet packages.

One solution you will find on the internet is to add these NuGet packages to your target projects. While this works it has got that yucky feel to it. Furthermore, this can lead to quite a bit of bloat since some projects come with sub-dependencies which will all show up in your NuGet package manager. But there is an easier way which is also less nauseous

Migrating to the newer Package Reference style can be done by simply right clicking on your packages.config and selecting Migrate packages.config to PackageReference...:

Visual Studio dialog showing Migrate To PackageReference

This action will change the csproj from this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="..\..\packages\Xamarin.Forms.\build\netstandard2.0\Xamarin.Forms.props" Condition="Exists('..\..\packages\Xamarin.Forms.\build\netstandard2.0\Xamarin.Forms.props')" />
  <!-- File includes and that stuff -->
    <Reference Include="System" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Core" />
    <Reference Include="Xamarin.Forms.Core, Version=, Culture=neutral, processorArchitecture=MSIL">
    <Reference Include="Xamarin.Forms.Platform, Version=, Culture=neutral, processorArchitecture=MSIL">
    <Reference Include="Xamarin.Forms.Platform.iOS, Version=, Culture=neutral, processorArchitecture=MSIL">
    <Reference Include="Xamarin.Forms.Xaml, Version=, Culture=neutral, processorArchitecture=MSIL">
    <Reference Include="Xamarin.iOS" />
    <ProjectReference Include="..\HelloReactiveUI\HelloNetStandard.csproj">
  <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
      <ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
    <Error Condition="!Exists('..\..\packages\Xamarin.Forms.\build\netstandard2.0\Xamarin.Forms.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.\build\netstandard2.0\Xamarin.Forms.props'))" />
    <Error Condition="!Exists('..\..\packages\Xamarin.Forms.\build\netstandard2.0\Xamarin.Forms.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Xamarin.Forms.\build\netstandard2.0\Xamarin.Forms.targets'))" />
  <Import Project="..\..\packages\Xamarin.Forms.\build\netstandard2.0\Xamarin.Forms.targets" Condition="Exists('..\..\packages\Xamarin.Forms.\build\netstandard2.0\Xamarin.Forms.targets')" />

To this:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- File includes and that stuff -->
    <Reference Include="System" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Core" />
    <Reference Include="Xamarin.iOS" />
    <ProjectReference Include="..\HelloReactiveUI\HelloNetStandard.csproj">
    <PackageReference Include="Xamarin.Forms">
  <Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />

Which only references the top level package. Further the package.config file will be removed. Since this method is still in development you might run into some bumps. So be sure to check out the limitations which might apply to the packages you are currently using. If that is the case make sure to let the team know. But once you have migrated, you will be able to use your project without the compile issue and all the benefits from using Nuget package references.


In this post, you saw how to migrate your Xamarin Projects to use package references. Using package references is intended to be the new adding NuGet package references directly to your csproj. With this migration, an error introduced when migrating your PCL projects to .Net Standard will also be solved. So be sure to check out this option when updating your Xamarin apps that have been out in the wild for a while.

Note: that all new projects created with Visual Studio 2017 will automatically use the package reference approach.

To see the sample app in full you can check them out on GitHub packages.config and package reference.

Thank you to Pierce Bogganwho pointed me in the right direction to get this problem solved.


Image with red dots - intended to look fancy

Some apps require quite a bit of content which is fairly static but changes over time and then the app should adjust and provide the user with the new content. Let's assume we want an app that provides us with quotes and their authors. We could just add the quotes to our app but whenever we wanted to update the app we would have to redeploy our app to the store(s). This can range from an inconvenience to requiring technical expertise for updating the app for simply correcting such a simple thing as a comma. So it becomes evident that in these cases we would like to separate the content from the app itself.

Hosting content does not require running any logic on the server. We do not need any other service than that of a simple file share. With perhaps one or two additional requirements regarding security etc. but more on that later. And that is exactly what Azure Blob Storage can provide us with.

Setting up the blob storage

You are required to have an Azure Account to create a blob storage, the steps, therefore, you can find here. On Azure create a blob storage, under containers, create a Container if you haven't done so already and then upload your data to it. In this sample, we will upload a single JSON file.

Showing Blobstorage Container with one JSON File

In a real application, we could also provide multiple other files including videos and other static files. But for this simple demo, we will stick to a lonely JSON file. We can access the content by calling the URL:

Sample get request with Postman

Having something on a public server always raises questions about security and the sorts. So let's have a look at them.


So let's start with what you get out of the box. The easiest security is if your data is public. Like on a website but for your app. The default you can limit the anonymous access to your blob storage as follows:

  • read-only container: which will allow everyone to read at the container level i.e. "look at the directory" and list all the blobs within.
  • read-only blob: here the caller will have to know which blob he wants to open and is limited to reading the blob storage itself.
  • no read access for anonymous: this will restrict the access to authenticated parties only.

In our sample, we will stick with anonymous blob access. But if you are interested in adding some extra layers of security be sure to check out the Azure Storage security guide which explains the different methods of authentication from shared keys all the way to using Azure Active Directory (Azure AD) for securing the access of your data. Which in summary means Blob Storage is not only quick and convenient in the beginning but can also be modified to add some serious layers of protection.

The client


On the client, we will want to consume the hosted resource and use it in our app. You can do this rather simply within a .Net Standard library using JSON.Net as follows:

string quotesJson;
using (var httpClient = new HttpClient())
    var response = await httpClient.GetAsync("https://gnabberonlinestorage.blob.core.windows.net/alpha/quotes.json");
    quotesJson = await response.Content.ReadAsStringAsync();
_quotes = JsonConvert.DeserializeObject<List<QuoteInfo>>(quotesJson);

While the above sample works and will get us our data it is not really smart, it will always pull the entire file even if nothing has changed. Fortunately, Azure Blob Storage supports ETags which allows us to be smarter when creating the call, by adding the If-None-Match header to our request as follows:

public async Task Init()
    if (_quote != null) return;

    IsBusy = true;
    string quotesJson;
    using (var httpClient = new HttpClient())
        if(!string.IsNullOrEmpty(CurrentEtagVersion)) httpClient.DefaultRequestHeaders.Add("If-None-Match", CurrentEtagVersion);
        var response = await httpClient.GetAsync("https://gnabberonlinestorage.blob.core.windows.net/alpha/quotes.json");

        quotesJson = response.StatusCode == HttpStatusCode.NotModified
            ? ReadQuotesFromCache()
            : await response.Content.ReadAsStringAsync();

        UpdateLocalCache(response.Headers.ETag, quotesJson);
    _quotes = JsonConvert.DeserializeObject<List<QuoteInfo>>(quotesJson);

    IsBusy = false;

If the local and remote ETag match, we will not receive any data with the call leaving us with a very small data footprint for this call. The code handling the caching is shown below. Note that for accessing the preferences Xamarin.Essentials were used:

public string CurrentEtagVersion => Preferences.Get(EtagKey, string.Empty);

private void UpdateLocalCache(EntityTagHeaderValue eTag, string quotesJson)
    // Only update the cache if we need to
    if (eTag == null || CurrentEtagVersion == eTag.Tag) return;
    Preferences.Set(EtagKey, eTag.Tag);
    File.WriteAllText(_quotesFilename, quotesJson);

private string ReadQuotesFromCache()
    if (!File.Exists(_quotesFilename)) return string.Empty;
    return File.ReadAllText(_quotesFilename);

I will leave it there with this sample but since we are already storing the data in a local cache we could also consider making this app fully Offline capable. With Xamarin Essentials, which we are already using, we can check if we have a network connection and what kind of connection. This information allows us to decide if we want/can access the remote storage or rather load the data from the initial cache.

You can find the entire client sample code on GitHub.


In this post, we saw how you can use Azure Blob storage as a backend service to host the content of your app without having to implement any web server. You can add security layers to the storage. Tracking changes on the backend are provided out of the box via HTTP ETags.

But how much will this cost me? Probably less than you would think but check out the Blob Storage pricing to get your exact number.