This blog post is part of the F# Advent Calendar 2020. A big thank you to Sergey for organizing this year and be sure to check out the other blog posts - after reading this one .

Based on the Model View Update (MVU) pattern the Fabulous frameworks provides the means to write functional first mobile (and desktop), clients. If you are new to Fabulous, I would recommend you check out the post by Tim (the maintainer of Fabulous) or the official docs.

The backend is implemented as an ASP.NET Core web app which has SignalR enabled. You can see how to this here.

To show how you can use SignalR with Fabulous, we will implement a chat application. And I felt like calling it "Fabulous Chat" - see how is easy it is to do fabulous things with this framework ? So let's ignore the question: "Does this world really need yet another chat app?!" in the back of our minds and let's have a look at what we will implement in this app to highlight how you could use SignalR within in a functional first mobile app.

Let's assume we have the following requirements:

  • A user should identify himself by his name.
  • The user should be able to send messages.
  • A user should be able to read messages while the app is active.

In other words, two actions are entirely UI driven, the user enters his username and then enters the chat. The user types a message and then sends it by the push of a button. However, when it comes to being able to read messages, the event will be fired from the background. Further, the SignalR connection usually is established once and then used for the remainder of the session. So let's create a module in our app, which will contain all the operations regarding the SignalR interaction.

module SignalR =
    let connectToServer =
        // connect to SignalR service

    let startListeningToChatMessages (connection: HubConnection) dispatch =
        // receive messages

    let sendMessage (connection: HubConnection) (message: ChatMessage) =
        // send message

We connect to the service after the user has provided his name. Not because it is required per se. But if at some later point we decide to add some proper authentication this will not change the flow of the app. Without further ado, let's implement the login view.

Image showing the login view

The view we use for this part is as described below.

let view model dispatch =
        (title = "Login",
         content =
                 (verticalOptions = LayoutOptions.Center,
                  horizontalOptions = LayoutOptions.Center,
                  children =
                      [ View.Label(text = "Please enter your Username")
                            (text = model.Username,
                             maxLength = 15,
                             placeholder = "Please enter your username",
                             completed = (fun _ -> (loginUser dispatch)),
                             textChanged = fun e -> dispatch (UsernameChanged e.NewTextValue))
                        View.Button(text = "Login", command = fun _ -> (loginUser dispatch))

Once the user hits the Login button, we want to establish the SignalR connection.

let connectToServer =
    let connection =

    async {
        do! connection.StartAsync() |> Async.AwaitTask
        return connection

Since we will want to hold on to the connection, we will invoke a Command which will be evaluated in the Update method.

let update msg (model: Model) =
    match msg with
    // ... other message handling
    | Connected connection ->
        { model with
              SignalRConnection = Some connection
              AppState = Ready },
    // ... more message handling

Now, after the user is connected to the chat, we will present the chat view. This view allows the user to type a message, send it and read the responses or questions by other users connected to the service.

Image showing the chat view in action

Let's start with writing messages. Similar as with the username we again have an Entry and a button for sending the messages. Once the user sends a message, we invoke SendAsync in our SignalR module:

let sendMessage (connection: HubConnection) (message: ChatMessage) =
    async {
        let jsonMessage = JsonSerializer.Serialize(message)

        do! connection.SendAsync("SendMessage", jsonMessage)
            |> Async.AwaitTask

So thus far, we have connected to the SignalR service, and we can send messages to the server. But we are still missing one essential part, and that is how we can receive messages from the backend service. What we need to do is register a listener to a specific SignalR-method (called NewMessage). We can implement our function as follows:

let startListeningToChatMessages (connection: HubConnection) dispatch =
    let handleReceivedMessage (msg: string) =
        printfn "Received message: %s" msg
        dispatch (Msg.MessageReceived(JsonSerializer.Deserialize<ChatMessage>(msg)))

    connection.On<string>("NewMessage", handleReceivedMessage)

Now in the handler method, we will dispatch a command every time a new message is received from the SignalR service. So let's extend our login function, from the beginning, to not only create a connection but also register our receiver.

let loginUser dispatch =
    async {
        let! connection = SignalR.connectToServer
        dispatch (Msg.Connected connection)

        SignalR.startListeningToChatMessages connection dispatch
        |> ignore

        dispatch (Msg.LoggedIn)
    |> Async.StartImmediate

We pass in the dispatcher from the view method when registering. This allows us to dispatch a command, i.e. invoke the update method and add the new message to our list of chat messages:

let update msg (model: Model) =
    match msg with
    // ... other message handling
    | MessageReceived chatMessage ->
        { model with
              Messages = chatMessage :: model.Messages },
    // ... more message handling

And with that, the user will be able to send and receive chat messages with whoever is using the chat program at the current moment.


And that is how we can use SignalR in a Fabulous app and create a Fabulous Chat app. Perhaps we still have to go over the design and security to really earn that name .

What you also saw is how you can work with events that do not originate from the user's input but happen in the background. This technique can be used whenever you are using some code that gets invoked in the background while your app is doing other fabulous stuff.

You can find the complete sample of the app on GitHub.

Titlephoto by Tyler Lastovich from Pexels



Whether writing a mobile app with Xamarin Forms or native Xamarin.iOS/Xamarin.Android sometimes the requirements demand that your app updates as quickly as possible when something changes on the server. This might be a chat application, stock ticker or any monitoring app showing live data to a user of a process currently running in the backend.

Let's say we wanted to create a simple chat app. If we would go down the path of a traditional Create Read Update Delete (CRUD) app over HTTP, we might choose to poll the server every few seconds or so to read the latest value. While this approach delivers the results, it comes with some drawbacks. To name a few: making requests even though nothing has changed, climbing the leader board on the battery usage category plus your server gets hammered with requests only to tell them: "Sorry no news yet..." - so if not poll lets push. And this is where WebSockets come in.

The only problem with WebSockets is that the implementation in .Net is close to the metal. This results in an additional implementation effort having to be done by the developers. Luckily for .Net developers, there is SignalR which comes with all the boilerplate code you want around WebSockets. A web developer will also tell you about fall back options backed into SingalR. As a Xamarin developer, you will most probably never use those features. But the chances are good that you will be delighted by the ease of handling connections, channels or writing to specific connected clients.

SignalR is around for quite a while; it has been ported over to .Net Core and is .Net Standard compatible. This is excellent news since we can add the SignalR client directly into our Xamarin Forms app - no platform-specific/wrapper code required. But before we can start implementing our client, we will first have to create a SignalR enabled backend.

If you are new to SignalR, be aware that there is quite a big difference between SignalR and SignalR Core. If you are writing a new app today using ASP.NET Core or Azure Functions. You will want to use SignalR Core or else you will go on a nasty error hunt ending with your palm slapping against your forehead ‍🤦‍♂️

The Server

When implementing the backend, we can choose between two options. Either we implement SignalR using ASP.NET Core, or we decide to go with Azures SignalR Core Service. The later can be integrated into ASP.NET Core or an Azure Function app. The Azure option also comes with scaling capabilities - in other words, you get up to 1 Million simultaneous connections using SignalR out of the box.

For more detail on how to set up the Azure Functions and SignalR combo, you will find instructions in the official documentation.

For our simple chat application, we will want a way for our clients to send and receive messages. To achieve this, we will have to create a SignalR Hub that provides a method for sending messages:

public static Task SendMessage(
    [HttpTrigger(AuthorizationLevel.Anonymous, "post")]string message,
    [SignalR(HubName = "SignalRDemo")]IAsyncCollector<SignalRMessage> signalRMessages)
    return signalRMessages.AddAsync(
        new SignalRMessage
            Target = "NewMessage",
            Arguments = new[] { message }

On invocation, the method will send the message to all connected clients. Which brings us to our next point. Every chat participant will first have to connect to the hub so that messages can be received. So let's implement that registration method:

public static SignalRConnectionInfo Negotiate(
[HttpTrigger(AuthorizationLevel.Anonymous)]HttpRequest req,
[SignalRConnectionInfo(HubName = "SignalRDemo")] SignalRConnectionInfo connectionInfo)
    // connectionInfo contains an access key token with a name identifier claim set to the authenticated user
    return connectionInfo;

The naming of the method is a convention from SignalR. In other words, you must name the method Negotiate, or your code will not work. No, I do not want to elaborate on how I found this one out the hard way 😉

With the function and SignalR Service in place, we can now turn our focus to the client.

The mobile client

On the mobile client, we want to be able to receive messages and type responses to the group. Our simple app will have to live with the limitation of only receiving messages while being connected. At least for the moment. But here is the chat running in all of its glory.


Now let's have a look at the ChatService which connects us to the backend and receives messages:

public async Task Connect()
    if (_connection.State == HubConnectionState.Connected) return;

    _connection.On<string>("NewMessage", (messageString) =>
        var message = JsonConvert.DeserializeObject<Message>(messageString);

    await _connection.StartAsync();

Note that we register the receiver method before we connect to the backend. This way, we start receiving updates as soon as being connected to the SignalR Service. Now when implementing a receiver method, you must ensure that the type signature matches the method we defined earlier on the server. If the types or the name do not match, you will never receive any messages.

Since reading is only half the fun, let's implement the send message:

public async Task Send(Message message)
    var serializedPayload = JsonConvert.SerializeObject(message);

    var response = await _httpClient.PostAsync("https://gnabbersignalr.azurewebsites.net/api/SendMessage", new StringContent(serializedPayload));
    Debug.WriteLine(await response.Content.ReadAsStringAsync());

And if you ever had enough from the stream of messages but want to give your eyes the joy of staring at a bare app here is the disconnect method:

public async Task Disconnect()
    await _connection.DisposeAsync();

    _connection = new HubConnectionBuilder()

I hope you could see that using SignalR it is a breeze to implement a bidirectional communication layer to your server. Which will allow your (mobile) clients to send and receive data in near real-time. Another side effect of using SignalR is that you could easily extend the app with a web client. Since your favourite JavaScript framework will allow you to use the SignalR client. If you are ready to get started with SignalR, be sure to check out the docs.

You can find the entire sample, including all the UI code on GitHub.

This blog is part of the October Xamarin Challenge. So be sure to check out the other posts for more best practices when writing Xamarin apps.



Ever since I have received my Azure IoT Devkit, I wanted to create a small app with it. The app would let me play around with streaming data from the device and view it on my phone. Also sending data to the device and many other exciting aspects of creating an IoT application. So what better way than to create a system that would stream the sensor data from my devkit to my mobile phone. Once that is implemented, how about creating an alerting system. Should a value increase over a certain threshold, the system will notify me. Many exciting ideas and paired with the capabilities of Azure all in my grasp. So before implementing, let me show you my high-level system design:

System Overview showing the IoT Kit, the different Azure services and the Xamarin mobile app.

However, first we will need to create an app on the device or emulate a device that sends up a stream of data to the Azure cloud over the IoT Hub. Then we will probably want to stream the data to the mobile client using SignalR. And finally visualise all of the data using some nice Xamarin Forms code - perhaps even a fancy charting library.

Once the data is getting streamed to the client let's have a look at alarming scenarios. What if the value e.g. the humidity rises quickly over a short timeframe. We should be able to detect such an event using Stream Analytics and then send a push notification to a client.

SignalR vs IoT Hub

Before diving into the implementation I showed this overview to a friend of mine - to make sure I am not overdoing it with the Azure-candy store. And promptly I got the question: "Hey Mark, so what you are basically doing is a Publish and Subscribe system - why do you need SignalR and IoT Hub. Wouldn't IoT Hub or SignalR offer all you need to send messages at scale to clients?". What a great question - and simplification of the problem. While it is right that what we are doing is nothing else then publishing data and subscribing to it. They are two worlds we are combining here. On the one side we have the IoT world. Here we the creator/company/enterprise produce the software and often also distribute the hardware. So we own the device and software. On the other hand when we release an app we often do not own the device on which the app is running on. So while we generally tend to trust our IoT devices because we create them, harden them and equip them with certificates for communication with our backend - in our case Azure IoT Hub. We generally do not have the same trust in mobile apps. Mobile apps are often deployed to the mobile store and therefore have no guarantee that the app does not end up on a rooted Android or Jailbreaked iOS phone. Once on such a device is often just a cat and mouse game to make it as difficult as possible for an attacker to access the secrets that got installed with the app. In short IoT apps and mobile apps come from a different starting position and therefore we tend to use different approaches for implementing security. And that is why we use two pub/sub systems, because both of them were designed with the two different aspects in mind.

In the next blog post we will get started with writing the IoT client and also see how we can write an IoT client without requiring a devkit.


Showing a wodden peer and the ocean on a sunny day

Fabulousallows writing Xamarin Forms apps with F#in a functional style. Fabulous has been inspired byElm, which proved that by using the Model View Update (MVU for short) pattern functional languages are great for writing UI code. While functional code squashes a plethora of potential bugs around null and race conditions - in this post, we will not focus on that aspect of Fabulous. Instead, let us look at how you can create beautiful UIs that stay maintainable in the future.

This blog post is part of the Xamarin UI July organised bySteven Thewissen. Be sure to check out all the beautiful posts by myfellow co-authors.

Featured #XamarinUIJuly Badge

Inspired by some of Stevens previous posts on - what I like to call - lickable UI. I wanted to show why writing UI in code allows you to write UI that is not only beautiful but easy to maintain and extend. So for this post, I will be implementing a design idea I found onDribbblebyApptaste.

App design as shown on dribbble

Though this blog post will focus on Fabulous, you could apply the same principle when writing your app using C#and XAML. But you will end up with a bunch of files, and it will feel more complicated. F#a terse language to write to begin with and Fabulous allow for writing apps with fewer lines of code than what you usually require with C#and XAML. I am not saying this is what should be your reason to check out Fabulous. But it is a fact that... If you are new to F#and Fabulous next comes a short intro. If this is all old news to you feel free to skip the intro.

A short intro to Fabulous

Let's start with the good oldWelcome to Xamarin Formsblank app:

module App = 
    type Model = 
      { Message : string } // your apps state, we could do without...

    type Msg = 
        | SomeStateChange // just for the demo, we do not need this...

    let initModel = { Message = "Welcome to Xamarin.Forms!" }

    let init () = initModel, Cmd.none

    let update msg model =
        match msg with
        | SomeStateChange -> model, Cmd.none

    let view (model: Model) dispatch =
          content = View.StackLayout(
            children = [ 
                View.Label(text = model.Message, horizontalOptions = LayoutOptions.Center, verticalOptions = LayoutOptions.CenterAndExpand)

    // Note, this declaration is needed if you enable LiveUpdate
    let program = Program.mkProgram init update view

type App () as app = 
    inherit Application ()

    let runner = 
        |> Program.withConsoleTrace
        |> XamarinFormsProgram.run app

Yes, this is all the code you would usually have in your blank C#app. We will not go into too much detail on how all the functions work. At the bottom, you can see thetype App, which translates to theApp.xaml.csclass, i.e. the entry point of any Xamarin Forms app. Our analogue to theMainPageis themodule App. The three components of the MVU pattern are present with theModel(an F#record, if your new to F#think of it as a POCO, not quite the same but close enough for now) and theviewandupdatefunctions.

The update function is where all changes of the view are processed. Displaying only text this function has nothing to do really. Since we will be focusing on the UI later, I will give you a short intro to what the update function does in your average app. Imagine all your UI changes and background task events must gosequentiallythrough this point. All state changes are defined. You can reproduce every state of the app - oh and no race conditions

The view function contains theContentPage, which includes aStackLayoutand aLabel. At first, you might not think much about it. But look how terse it is written. For example, theStackLayoutchildren, that is a simple list in F#. So adding another element to the grid would be simply adding a new UI element.

The functions get invoked by Fabulous and do not interact with Xamarin Forms directly. This is important to understand because this means that all of the code you write can be 100% unit tested. All the dependencies to the view are resolved within the Fabulous framework. Theviewfunction returns the instructions on how to create the UI, but it does not create it. If you change a value such as the welcome message, the Fabulous Framework checks what parts have changed and updates the view accordingly. The React.JS framework uses the same technique with a shadow DOM (Document Object Model) that is then taken to update the actual UI.

Atomic Design and coded UIs

Writing your UI with code comes with a few perks. While you could write all of your UI in theviewfunction. It might get a bit hard to view at a glance over time. But being only code, you can split up the code into different functions. This also allows you to reuse parts of the UI in different places. And reusability reusing/combining components is at the heart of Atomic Design.

Atomic Design

While unique design, reusable components sound all great, let's have a look at how we could design such an app with Fabulous. We want to start with the essential elements (Atoms) which we then put together to more significant UI components and in the end the Page.

When we look at the design of the app, we can see that most of the title labels seem to have the same font. Another UI component that quickly gets my eye is the cards holding the description of the destination and the things to do:


Now what we can see is that titles have the same font, are bold and apart from the cards in the"Things to do"section have the same font size. So let's create a function that allows us to create a title label with the parameters text and font size:

let titleLabel text fontSize =
    View.Label(text = text,
        fontSize = fontSize,
        textColor = textColor,
        verticalOptions = LayoutOptions.Center,
        fontAttributes = FontAttributes.Bold)

The destination is shown by a picture (gorgeous pictures if I may say so! ) and a short description of the town, country, rating and a favourite It seems that the favourite should be clickable so let's assume that is a button. Probably similar to the search button on the top right. Keeping accessibility in mind, I prefer to use buttons or platform interactive controls in general if an interaction is required by the user. This way, it will be easier to optimize the experience using a screen reader. So we want a button with an icon - or a text. Since Xamarin Forms allows us to use custom fonts, we could use a font such asFont Awesometo provide us with scalable icons. Be sure to check outJames'poston how to use Font Awesome with your Xamarin Forms app. So let's create a function that given the icon, colour, background colour and command function returns us with the button:

let materialFont =
    (match Device.RuntimePlatform with
                             | Device.iOS -> "Material Design Icons"
                             | Device.Android -> "materialdesignicons-webfont.ttf#Material Design Icons"
                             | _ -> null)

let materialButton materialIcon backgroundColor textColor command =
    View.Button(text = materialIcon,
        command = command,
        fontFamily = materialFont,
        fontSize = 20.,
        backgroundColor = backgroundColor,
        widthRequest = 42.,
        textColor = textColor)

So now to the description text i.e. the country. Lets again create a function that will create a label given the text:

let descriptionLabel text =
    View.Label(text = text,
        textColor = secondaryTextColor,
        fontSize = descriptionFontSize

Did you notice the title and description pattern is repeated in the"Things to do"section of the page. Up to now we have created what Atomic Design calls Atoms. Now let's pack some of those atoms into a coherent block such (Molecule):

let titleAndDescription title titleFontSize description =
    View.StackLayout(margin = 0.,
            titleLabel title titleFontSize
            descriptionLabel description |> fun(label) -> label.Margin (Thickness(0.,-8.,0.,0.))]

This will allow us to reuse the Title & Description duo further. Also, note that we had to adjust the margin a bit. You can think of the|>as a pipe forward. Since we have a View type, we can pipe it forward to a lambda function where we change the margin. Calling the margin function will again return a View type. If you are using LINQ, you most probably have joined multiple calls to where select et al. - we are doing the exact same thing here.

Now looking back at the short description of the destination, we can also see a rating of the city with stars. So let's create a function that given the icon and text colour returns aLabelbased on font awesome.

let materialIcon materialIcon color =
    View.Label(text = materialIcon,
        textColor = color,
        fontFamily = materialFont,
        fontSize = 18.,
        verticalOptions = LayoutOptions.Center,
        fontAttributes = FontAttributes.Bold)

The rating bar - I assume it is a read-only indicator that shows me an overall rating between zero to five. Given a rating of 4.5, we want four full stars and one covered by half. So let's take this control apart, let's say we want one function that only draws the star for a certain percentage:

let ratingStar percentage =
    let star = materialIcon star starColor
    let boxViewWidth = 16. - (16. * percentage)
        padding = 0.,
        margin = Thickness(0.,-4.,0.,0.),
        children = [
            View.BoxView(color = backgroundColor, 
                widthRequest = boxViewWidth,
                isVisible = (if percentage > 0. then true else false),
                horizontalOptions = LayoutOptions.End)

The function aka star factory is called by another function that draws N stars given the rating:

let ratingControl (rating:decimal) =
    let fullNumber = Math.Ceiling(rating)
    let fraction = (rating - Math.Truncate(rating))
    View.StackLayout(orientation = StackOrientation.Horizontal,
        children = [
            for i in 1m .. fullNumber -> if i = fullNumber then ratingStar (float fraction) else ratingStar 1.

Now we have all of our building blocks together for the description, but we still have the image with rounded corners left. A quick look at theImageViewfrom Xamarin Forms tells us:"No rounded edges."But when putting the image in aFrame, we can create the rounded edges effect. So let's create a function that gives us an image with round corners:

let roundedCornerImage imagePath =
    View.Frame(cornerRadius = cornerRadius,
        padding = 0.,
        isClippedToBounds = true,
        hasShadow = true,
        content = View.Image(
            source = imagePath,
            aspect = Aspect.AspectFill)

The parts are all made now let's assemble them so that we get the Image with rounded corners overlaid by a short description:

let cityDescriptionFrame city dispatch =
        margin = Thickness(16.,0.,16.,0.),
        children = [
            (roundedCornerImage city.Image |> fun(img) -> img.HeightRequest 320.)
                heightRequest = 70.,
                margin = Thickness(24.,-64.,24.,0.),
                padding = Thickness(20.,12.,16.,12.),
                backgroundColor = Color.White,
                cornerRadius = cornerRadius,
                content = View.Grid(
                    rowdefs=["auto"; "auto" ],
                        (titleAndDescription city.Name titleFontSize city.Country)
                        (favoriteIcon city dispatch).GridColumn(2)
                        (ratingControl city.Rating).GridRow(1).GridColumnSpan(2)
                hasShadow = true)

Similarly, we can implement the"Things to do"section. The great thing is we can reuse a lot of components that we have already created. Then we can put all the parts together in the view method which presents us with the following UI:


You can find the entire sample onGitHub.

Side notes: No, we are not required to have all the code in one file. But since this is a one-pager application, I left it together, so it is easier to navigate the code in a browser. Further note that theCarouselViewdid not work correctly when I was working with the view. I hope I will be soon able to get it working and have a sample which will allow switching between cities as intended by design.


Applying the Atomic Design pattern to your UI can really make your app easier to maintain and create. Given that Fabulous allows writing your UI in code, it is relatively straight forward to create a custom and consistent UI without much boilerplate code. Further Fabulous offers a live update feature which allows you to live code during a debug session. Not only does the UI adapt, but also the logic is executed. You can read more aboutthe live update featureon the official site.

It seems that writing UI with code is coming back into style during the recent days of 2019. With companies like Apple working on Swift UI. If you are a die-hard C#lover, you should check out thePost by Ryan Davison writing UIs with C#for Xamarin.

You can read more about the Atomic Design pattern onBrad Frosts website.


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