Xamarin with macOS 10.14 (mojave)

It’s that time of year again where we all ask ourselves “should I install this beta software on my devices and risk my development setup?”. If you’ve only one iPhone and Mac then it can be difficult to decide when it’s the right time to install the latest and greatest offerings from Apple, for fear of breaking your development environment.

This year I’ve not needed to be so worried about breaking my environment as my current projects see me developing more with ASP.NET and Swift than Xamarin, so I went ahead and downloaded both iOS 12 and macOS 10.14 as soon as I could.

Screenshot 2018-06-05 at 00.03.18

How does it run?
First impressions of running macOS Mojave and Xamarin are better than expected! I fired up Visual Studio for Mac and opened the workshop myself and Robin-Manuel Thiel created to see if I could get the iOS app to build using Xcode 9. The good news is that it works without any modification assuming you had a working setup before upgrading.

With that said, I have experienced some crashes with resizing VS4Mac but issues are expected at this stage.

Screenshot 2018-06-05 at 00.26.15

Advice
If you can avoid it, don’t update just yet if your day-to-day development requires the Xamarin tooling to work. The Xamarin engineers will need some time to test and ensure things work properly as they too have only just downloaded a copy of the OS.

On the other hand, if you simply cannot wait to play with macOS Mojave,  then know that at a minimum, you can continue to build and deploy with the latest beta software from our friends in Cupertino.


– Opinions are my own and not the views of my employer

Updated Resilient Networking with Xamarin

Rob Gibbons wrote a fantastic blog post back in 2015 on how best to write network requests layers for your Xamarin Apps. I’ve personally used this approach many times, but I felt that it needed updating for 2018, so here it is. A slightly updated approach to resilient networking services with Xamarin. And when I say ‘slightly update’, I honestly mean it’s a minor change!

we-dont-throw

Refit

For those of you who are familiar with Rob’s approach, he uses pulls together a few libraries to create a robust networking layer. One of the critical elements of his strategy is the use of Refit. Refit is a REST library which allows us to interact with remote APIs with minimal boiler-plate code. It makes heavy use of generics and abstractions to define our REST API calls as a C# Interfaces which are then used with am instance HTTPClient to handle all the requests. All serialisation is dealt with for us! I still believe Refit to be a great library to use so we’ll keep this as the core of this pattern.

Let’s have a look at an example interface for use with Refit.

public interface IBeerServiceAPI`
{
    [Get("/beer/")]
    Task GetBeers();
}

We use attributes to define the request type as well as its path (relative to the HTTPClients base URL).

We then define what we expect back from the API and leave Refit to handle making the call, deserialising the response and handing it back to us as a concrete type.

To expand on this, we can add many more types of requests.

[Get("/beer/{id}/")]
Task GetBeerById(string id);

[Post("/beer/")]
Task CreateBeer([Body] Beer beer);

[Delete("/beer/{id}/")]
Task DeleteBeer(string id);

[Put("/beer/{id}/")]
Task UpdateBeer(string id, [Body] Beer beer);

We can now use the interface to make calls to our remote endpoint. I usually place these methods within a class that is unique to the service I’m calling. I’m this example; it’d be a “BeersService.”

//Create new beer item
public async Task<Beer> CreateBeerAsync(Beer beer)
 {
    var apiInstance = RestService.For<IBeerServiceAPI>(Helpers.Constants.BaseUrl);
    return await apiInstance.CreateBeer(beer);
}

//Get by ID
public async Task<Beer> GetBeerByIdAsync(string id)
{
    var apiInstance = RestService.For<IBeerServiceAPI>(Helpers.Constants.BaseUrl);
    return await apiInstance.GetBeerById(id);
}

That’s all it takes for us to starts interacting with a remote API. If you’re wondering how to test this, it’s incredibly easy to swap out implementations with mock services when using this architecture!

Resiliency

Building a resilient networking service requires a few things. We need to understand what our current connectivity looks like, as well as find a solution for caching data locally to ensure our app still ‘works’ in offline situations.

We can achieve both of these tasks by leveraging packages from Motz. He’s created a plugin for checking connectivity status as well as developed a library for caching.

Lets first take a look at connectivity status.

You’ll want to add the Connectivity Plugin nuget package to every client project in the solution as well as the PCL. The following platforms are supported:

  • Xamarin.iOS
  • tvOS (Xamarin)
  • Xamarin.Android
  • Windows 10 UWP
  • Xamarin.Mac
  • .NET 4.5/WPF
  • .NET Core
  • Samsung Tizen

To use the connectivity plugin, we can simple make the following call:

var isConnected = CrossConnectivity.Current.IsConnected;

Caching

Now that we can check for connectivity, we detect that we’re offline. Let’s have a look at how to implement that.

public async Task<List<Beer>> GetBeersAsync()
{
    Handle online/offline scenario
    if (!CrossConnectivity.Current.IsConnected)
    {
        //If no connectivity, we need to fail... :(
        throw new Exception("No connectivity");
    }
    //Create an instance of the Refit RestService for the beer interface.
    var apiInstance = RestService.For<IBeerServiceAPI>(Helpers.Constants.BaseUrl);
    var beers = await apiInstance.GetBeers());

    return beers;
}

Returning no results for most requests isn’t a great solution. We can dramatically improve the user experience by keeping a cache of data to show in offline situations. To implement that, we’re going to use Monkey Cache. To use Monkey Cache, we have to first configure the ApplicationId.  A folder created for your app on disk with the ApplicationId, so you should avoid changing it.

Barrel.ApplicationId = "your_unique_name_here";

Adding Monkey Cache is super simple. First, of, we want to define a key. Think of this as the collection (barrel) name. After that, we implement the necessary logic to handle caching.

public async Task<List<Beer>> GetBeersAsync()
{
    var key = "Beers";

    Handle online/offline scenario
    if (!CrossConnectivity.Current.IsConnected && Barrel.Current.Exists(key))
    {
        //If no connectivity, we'll return the cached beers list.
        return Barrel.Current.Get<List<Beer>>(key);
    }

    //If the data isn't too old, we'll go ahead and return it rather than call the backend again.
    if (!Barrel.Current.IsExpired(key) && Barrel.Current.Exists(key))
    {
        return Barrel.Current.Get<List<Beer>>(key);
    }            

    //Create an instance of the Refit RestService for the beer interface.
    var apiInstance = RestService.For<IBeerServiceAPI>(Helpers.Constants.BaseUrl);
    var beers = await apiInstance.GetBeers());

    //Save beers into the cache
    Barrel.Current.Add(key: key, data: beers, expireIn: TimeSpan.FromHours(5));

    return beers;
}

Polly

Returning to Rob’s original post, we’ll want to add Polly. Polly helps us handle network requests sanely. It allows us to retry, and process failures robustly.

We’re going to use Polly to define a retry logic that forces the service to retry five times, each time waiting twice as long as before.

public async Task<List<Beer>> GetBeersAsync()
{
    var key = "Beers";

    Handle online/offline scenario
    if (!CrossConnectivity.Current.IsConnected && Barrel.Current.Exists(key))
    {
        //If no connectivity, we'll return the cached beers list.
        return Barrel.Current.Get<List<Beer>>(key);
    }

    //If the data isn't too old, we'll go ahead and return it rather than call the backend again.
    if (!Barrel.Current.IsExpired(key) && Barrel.Current.Exists(key))
    {
        return Barrel.Current.Get<List<Beer>>(key);
    }            

    //Create an instance of the Refit RestService for the beer interface.
    var apiInstance = RestService.For<IBeerServiceAPI>(Helpers.Constants.BaseUrl);

    //Use Polly to handle retrying (helps with bad connectivity) 
    var beers = await Policy
        .Handle<WebException>()
        .Or<HttpRequestException>()
        .Or<TimeoutException>()
        .WaitAndRetryAsync
        (
            retryCount: 5,
            sleepDurationProvider: retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))
        ).ExecuteAsync(async () => await apiInstance.GetBeers());


    //Save beers into the cache
    Barrel.Current.Add(key: key, data: beers, expireIn: TimeSpan.FromSeconds(5));

    return beers;
}

Wrapping Up

This is a great way to implement your networking layer within your apps as it can sit within a .NET Standard library and be used in all your client apps.

If you’d like to see a more real-world example of this approach, then check out the Mobile Cloud Workshop I created with Robin-Manuel. The Xamarin.Forms app uses this approach and, it’s been working very well for us!

Big thanks to Rob for the original post and documenting such a simple solution to complex problem!