Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Cross-platform REST: HttpClient in Mobile Apps

DZone's Guide to

Cross-platform REST: HttpClient in Mobile Apps

Tips and tricks for making your .NET REST clients performant and easy to maintain, complete with code examples.

· Mobile Zone
Free Resource

I’ve been seeing a lot of discussions lately on how to correctly use HttpClient in mobile apps, both UWP and/or Xamarin apps. In this post, I’d like to share how I currently use HttpClient in my apps.

Read This First -> Disclaimer

This article discusses how I use the HttpClient class. If you use it in a different way or don’t agree with what I write here, feel free to start a constructive discussion in the comments. I’ll be happy to discuss your way of thinking vs mine and adjust the article where necessary.

To Single-instance or to Multi-instance

Back when I was getting into writing mobile apps (in the WP7 era), I started learning about REST services and how to call them from .NET code. I learned about HttpClient and quickly found out that it implemented IDisposable, so the logical step was the using keyword.

using (var client = new HttpClient())
{
    var result = await client.GetAsync(Constants.ApiCallUrl);
}

Turns out, not the best way to go at it. When you dispose an HttpClient instance, the connection to the server is closed. 

When you do the next call to the server, with a new HttpClient instance, that connection is reopened. This causes delay in getting a response from the web service. If a server really doesn’t want you to keep your connection open, it will inform you of that via a header and the HttpClient instance will quietly close the connection and reopen it the next time.

So, how do we create an easy to (re)use instance?

public class RestClient
{
    private static HttpClient _client;

    public static HttpClient GetClientInstance()
    {
        if (_client == null)
            _client = new HttpClient();

        return _client;
    }
}

And, to use this instance:

private async Task FetchData()
{
    var result = await RestClient.GetClientInstance().GetAsync(Constants.ApiCallUrl);
}

Use a Base URL (and Don’t Even Dare Making it a Magic String)

The HttpClient class has a base URL property. As you might have figured out, that property is meant to contain the root URL of your API. So, if we modify our HttpClient a bit we get this:

public class RestClient
{
    private static HttpClient _client;

    public static HttpClient GetClientInstance()
    {
        if (_client == null)
            _client = new HttpClient
            {
                BaseAddress = new Uri(Constants.ApiBaseUrl)
            };

        return _client;
    }
}

As for the magic strings remark, put all your string values into a constants class (or whatever you want to name it, as long as they’re all together.) If you don’t understand the reasoning behind this, just try writing applications with string values in the code, you’ll find out soon enough!

Use Compression Where Possible

Since we’re mostly writing mobile applications we need to keep data usage in mind. We have no idea if our users will have an expensive data plan, unlimited data, or if they’re on WIFI. This means that it’s our job as developers to keep the data usage as low as possible. The quickest win here is to compress the data from the server and decompress it on the device, this, of course, means that the server needs to support compression. If you maintain the server yourself, make sure that it is enabled (it’s enabled by default in Web API.) Once that is enabled, we need to tell the HttpClient to enable decompression from either Deflate or Gzip formats. Enable decompression is done through an HttpClientHandler object that we can pass into the HttpClient constructor.

 public static HttpClient GetClientInstance()
{
    if (_client == null)
    {
        var handler = new HttpClientHandler();
        if (handler.SupportsAutomaticDecompression)
        {
            handler.AutomaticDecompression = DecompressionMethods.GZip |
                                             DecompressionMethods.Deflate;
        }

        _client = new HttpClient(handler)
        {
            BaseAddress = new Uri(Constants.ApiBaseUrl)
        };
    }

    return _client;
}

Caching

Another often overlooked way of limiting data usage is caching. You’d be surprised of the times users request the same data, if we cache the API result we can just fetch it from that cache again. Extra usability feature here is that we can actually show results when the device is offline. You can write your own caching framework if you want, or use one of the existing ones. I tend to switch between Q42 (they save cached data in JSON files) and Akavache (they save cached data in SQLite).

Security Tokens

We often need to make API calls to secured services. To do this we need to go through some form of authorization/authentication flow where we get an access token from (a bearer token for example.) That token needs to be passed in with every API call we make. Since we’re now using a single instance for our HttpClient, it would be nice to specify the token once and be done with it. This can easily be done by using the default headers. HttpClient contains a collection of headers that it will use with every call it makes. This is how you add a Bearer token for example:

_client = new HttpClient(handler)
              {
                  BaseAddress = new Uri(Constants.ApiBaseUrl)
              };

_client.DefaultRequestHeaders.Clear();
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", UserInstance.AccessToken);

Use ConfigureAwait(false)

HttpClient is an async library. This means that we usually use this in a method that returns either Task or Task<T>. This also means that if you don’t use ConfigureAwait(false) that you’ll create quite a lot of context switching. When you await an async method, and don’t specify ConfigureAwait(false), the method will do it’s thing on the thread pool and switch back to the caller’s context when finished. This is exactly the behavior that you want when you request a webresult and immediately put the data into a property that it's bound against, since binding happens on the UI thread. But, this is not what we want when we’re executing code in a library or in a service class, so that’s where we’ll use ConfigureAwait(false).

Let’s say, for example, that we have a method to fetch all resources as a JSON string. The correct way of using ConfigureAwait(false) would be:

private async Task FetchData()
{
    var service = new ResourcesService();
    var result = await service.FetchAllResources();

    //textblock is bound against Json
    Json = result
}

public class ResourcesService
{
    public async Task<string> FetchAllResources()
    {
        var client = RestClient.GetClientInstance();
        var result = await client.GetAsync(Constants.ApiCallUrl).ConfigureAwait(false);
        string json = await result.Content.ReadAsStringAsync().ConfigureAwait(false);

        return json;
    }
}

The FetchData method doesn’t use ConfigureAwait(false) because it needs to return to the caller context. The caller context here is the UI thread. The property that the returned value is being set to will trigger a change notification, so we need to be on the UI thread.

The FetchAllResources method has two calls that are awaited, by not returning to caller context in that method we prevent two context switches to occur.

(Xamarin Only) Use ModernHttpClient

Xamarin allows us to write iOS and Android applications in C#. We can use the HttpClient for that but we'd be using an abstraction of the lowest common denominator of both platforms. If you use ModernHttpClient on those platforms instead, you will get the full networking stack of the respective platforms. (thanks for reminding me @Depechie)

Conclusion

In this post, I discussed some techniques for using HttpClient to fetch data in mobile applications that I’ve picked up over the years. As I’ve mentioned in the disclaimer, this is what I’m doing today and what feels right to me. Feel free to let me know if you do things another way. If you can convince me that your way is better, I will have learned something and will update the post accordingly.

Topics:
http ,.net ,xamarin ,mobile

Published at DZone with permission of Nico Vermeir, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}