Xamarin Forms, Contoso Cookbook, and Azure: Three Great Tastes that Taste Great Together

My previous post introduced a Xamarin Forms version of Contoso Cookbook that runs on Windows Phone, Android, and iOS. For the benefit of developers learning Xamarin Forms – especially those already versed in Microsoft XAML – it introduced the basics of navigating between pages in a multipage app with NavigationPage and creating tabbed pages with TabbedPage. It also introduced ListView as a means for binding to collections and data templates for specifying how items in those collections should be rendered.

That version of Contoso Cookbook relied on local data – one JSON file plus a collection of images – embedded in the Portable Class Library (PCL). In real life, data such as this typically comes from the cloud. Therefore, I thought it might be useful to modify Contoso Cookbook to pull data from Microsoft Azure. That’s the subject of this article.

The revised version of Contoso Cookbook doesn’t look any different on the outside: It still uses a TabbedPage to present a list of recipes and recipe groups, and it still uses NavigationPage to navigate to a ContentPage showing recipe details when a recipe is tapped. You may notice that it takes longer for the recipes and recipe groups to appear, however, because the data is no longer loaded locally. Instead, it comes from Azure storage repositories that I created in the cloud. The code changes required were minimal, and yet there are a couple of lessons that aspiring Xamarin Forms programmers can take away. One involves doing asynchronous networking using C#’s awesome await operator without the benefit of awaitable methods. The other involves presenting some kind of status indicator letting the user know that data is being downloaded, in case that download takes longer than expected.

HTTP Networking in a Xamarin Forms PCL

When you create a portable Xamarin Forms solution in Visual Studio, the solution includes a PCL project containing shared C# and XAML. Inside that PCL, you have access to a subset of the .NET Framework Class Library. Which subset you have access to – and therefore which APIs you can use – depends on the PCL’s profile. That profile is driven by the collection of platforms that the PCL targets. You can see which platforms are targeted by right-clicking the PCL project, selecting “Properties,” and looking at the list of targets in the “Targeting” section of the “Library” tab:

image

Furthermore, you can change the list of targets by clicking the Change button:

image

Generally speaking, the more boxes you check, the more restrictive the API. More targets means a smaller cross-section of APIs implemented on all the platforms.

That’s all fine and good. But what does it have to do with HTTP networking? Simple. With the default PCL profile, you can use the HttpWebRequest class in the System.Net namespace, but you can’t use the handier and more modern HttpClient class in the System.Net.Http namespace.

Most C# developers greatly prefer HttpClient to HttpWebRequest for the simple reason that HttpClient features awaitable methods such as GetAsync and GetStringAsync – methods that can be awaited with C#’s await operator. HttpWebRequest, by contrast, uses the old Begin/End async pattern and doesn’t support await. I don’t know about you, but after getting used to HttpClient while writing Windows Store apps and Windows Phone apps, I never want to do HTTP networking the old way again. But HttpClient isn’t available in a Xamarin Forms PCL – or is it?

Turns out it is – sort of. One way to make HttpClient available to Xamarin Forms PCLs is to uncheck the “Windows Phone Silverlight 8” box in the Change Targets dialog. It works, but while you can still build apps for iOS and Android, you can’t build them for Windows Phone.

Another way to do it is to download and install the Microsoft HTTP Client Libraries from NuGet. But I was reluctant to do that since the Xamarin forums are full of complaints from people who did.

So what do you do if you want to do awaitable networking but don’t care to 1) throw away support for Windows Phone, or 2) install a separate library? Easy: you use the Task class in the System.Threading.Tasks namespace, which is available in a PCL using the default profile, to put an awaitable wrapper around HttpWebRequest. In the original version of Contoso Cookbook, I extracted the JSON file containing recipe data from the PCL like this in App.cs:

// Read RecipeData.json from this PCL's DataModel folder
var name = typeof(RecipeViewModel).AssemblyQualifiedName.Split(',')[1].Trim();
var assembly = Assembly.Load(new AssemblyName(name));
var stream = assembly.GetManifestResourceStream(name + ".DataModel.RecipeData.json");

// Parse the JSON and generate a collection of RecipeGroup objects
using (var reader = new StreamReader(stream))
{
    string json = await reader.ReadToEndAsync();
      ...
}

In the new version, I downloaded the same JSON data from Azure:

// Retrieve JSON recipe data from Azure
var request = HttpWebRequest.Create("http://jeffpro.blob.core.windows.net/contoso/Recipes");
var response = (HttpWebResponse) await Task.Factory.FromAsync<WebResponse>
(request.BeginGetResponse, request.EndGetResponse, null);

if (response.StatusCode != HttpStatusCode.OK)
    throw new WebException(response.StatusDescription);

// Parse the JSON and generate a collection of RecipeGroup objects
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
    string json = await reader.ReadToEndAsync();
      ...
}

This is equivalent to having your cake and eating it, too. You retain compatibility with Windows Phone and enjoy awaitable networking, too.

Incidentally, you may recall that in the previous version of Contoso Cookbook, a custom value converter was required to convert image resource IDs into ImageSource objects that could be assigned to image views. That converter is no longer needed when the images themselves are downloaded over HTTP. In other words, a Xamarin Forms Image is perfectly happy to accept a URL like this one for its Source property:

<Image Source="https://training.atmosera.com/devcenter/wp-content/uploads/2015/03/pizza-650x401.png" />

But the image URLs in the JSON recipe data downloaded from Azure are relative URLs such as “images/chinese/noodles.jpg.”  So I wrote a simple value converter named AzureImagePathConverter to convert each image URL into an absolute URL by prefixing it with the string “http://jeffpro.blob.core.windows.net/.”

Displaying an Activity Indicator

One downside to networking is that you never know when – or if – a call will complete. Therefore, it ‘s a good idea to let the user know something is happening while you wait. Otherwise, if it takes a while, he or she may just assume that the app isn’t working.

Xamarin Forms offers a handy control (or “view” in Xamarin parlance) called ActivityIndicator for precisely this purpose. You can declare an ActivityIndicator in a page and set its IsRunning property to true just before putting a call on the wire. Then set IsRunning to false when the call completes. For the duration of the call, the user will see a platform-specific activity indicator indicating that you’re waiting for something to finish.

Alas, ActivityIndicator is of little use to Contoso Cookbook. The cookbook’s main page is a TabbedPage wrapped in a NavigationPage, and neither TabbedPage nor NavigationPage has a Content property that would allow you to put an ActivityIndicator on the page. You could embed the ActivityIndicator in one of the ContentPages in the TabbedPage’s item template, but you’d never see it because those pages aren’t rendered until the data is downloaded and bound to the TabbedPage.

The solution is a property in every Xamarin Forms page called IsBusy. Setting IsBusy to true displays a global activity indicator (Windows Phone, Android, and iOS each have one) independent of anything else displayed by the app. Contoso Cookbook’s top-level page is the NavigationPage created in App.cs. So I modified the code to set the NavigationPage’s IsBusy property to true before calling out to Azure, and to false when the call completes:

try
{
    _rvm = new RecipeViewModel();
    this.MainPage.IsBusy = true;
    await _rvm.LoadRecipesAsync();
}
catch (Exception)
{
    throw;
}
finally
{
    this.MainPage.IsBusy = false;
}

The purpose of the try/catch/finally block is to make sure IsBusy gets reset to false, even if the call fails and an exception is thrown.

Here’s how the global activity indicator looks. It’s at the top of the screen on Windows Phone (the dots chasing each other across the screen), in the upper-right corner on Android, and at upper left on iOS.

image

Summary

I could say a lot more about HTTP networking in Xamarin Forms, and in the future, I will. But there are more pressing matters to consider right now, such as dressing up the app’s main page with recipe descriptions under the recipe titles. It sounds simple, but in fact it poses a problem whose solution will require a deeper dive into Xamarin Forms. We’ll begin that deep dive together in the next article.

Xamarin-Partner_thumb1_thumb

Need Xamarin Help?

Xamarin Consulting  Xamarin Training

Stay Informed

Sign up for the latest blogs, events, and insights.

We deliver solutions that accelerate the value of Azure.
Ready to experience the full power of Microsoft Azure?

Atmosera is thrilled to announce that we have been named GitHub AI Partner of the Year.

X