Just Use Akavache

There comes a time in every application's (iOS, WPF, Android, Windows Phone) life where it makes sense to start caching data to disk. When this time comes, I like to use Akavache. Akavache is "An Asynchronous Key-Value Store for Native Applications." That's a little bit of jargon. To put it simply, akavache is a way to write data to disk without locking up the UI of an application. I use it when writing non trivial apps with Xamarin.

Getting set up is super easy. Just add a reference to your project via nuget.

image of nuget

When I use Xamarin Studio, I get this error. To solve it I add a reference to Akavache.SQLite3 and then Akavache installs fine.

image of error

installing Akavache.SQLite

  • First things first, name your application. I do this in the startup seam of whatever platform I am integrating Akavache into.

            BlobCache.ApplicationName = "AkavacheDemo";
    
  • Let's create a model.

    public class Airport
    {
        public string code { get; set; }
        public string name { get; set; }
        public string location { get; set; }
        public string comments {get;set;}
    }   
    
  • I like to separate things out, so I created a DataCache class in a Platform Specifics folder.

Data Cache Creation

Inserting Data:

 public async Task StoreAirport(Airport airport)
    {
        await BlobCache.LocalMachine.InsertObject(airport.code, airport);
    }

You might get an error here that says "Cannot await 'System.IObservable System.Reactive.Unit'."

image of error

To solve this, the current documentation says add using statement (this is not a knock on the documentation, it is great).

using System.Reactive;

However, I still got the "Cannot await 'System.IObservable System.Reactive.Unit'" exception from Xamarin Studio. To fix this, I changed the using statement to:

using System.Reactive.Linq

Getting Data:

    public async Task<Airport> GetAirport(string airportCode)
    {
        try
        {
            return await BlobCache.LocalMachine.GetObject<Airport>(airportCode.ToLower());
        }
        catch(KeyNotFoundException ex)
        {
            //for now we want to have the service go get information
            return null;
        }
    }
  • The webservice leverages another awesome library Modern Http Client

    private const string _endpoint="http://airportcode.riobard.com/airport/";
    private const string _dataType="?fmt=JSON";
    
    
    public async Task<Airport> GetAirportByCode(string code)
    {
        using (var client = new HttpClient(new NativeMessageHandler()))
        {
            var request = string.Format("{0}{1}{2}", _endpoint, code, _dataType);
            var result =  await client.GetAsync(request);
    
    
    
        using (var reader = new StreamReader(await result.Content.ReadAsStreamAsync()))
        {
            return JsonConvert.DeserializeObject&lt;Airport&gt;(await reader.ReadToEndAsync());
        }
    }
    
    }

That's it for the internals.

I built a very simple application to wrap the Akavache cache and webservice.

The UI markup is available here. And looks like this:

image of UI

Once we get our buttons for saving and UI elements, we add the following code to the main activity to get an airport.

var airport = await _cache.GetAirport(airportCode.Text);
                if (airport == null)
                {
                    airport = await _service.GetAirportByCode(airportCode.Text);
                }
                AndHUD.Shared.Dismiss();
                if(airport != null)
                {
                    code.Text = airport.code;
                    name.Text = airport.name;
                    location.Text = airport.location;
                    comment.Text = airport.comments;
                    //insert that data
                    _cache.StoreAirport(airport);
                }

And to save an airport and comment.

var airportToSave = new Airport()
                {
                    code=code.Text,
                    location = location.Text,
                    name = name.Text,
                    comments = comment.Text
                };
                await _cache.StoreAirport(airportToSave);

The result is a smooth application that uses Akavache to cache search data and then gets it from the cache when requested without making a network call.

image of application searching and saving

In Conclusion

We've reviewed the basics of using akavache to cache and get cached data. In my experience, the most important thing is to add the following statement to your cache class.

using System.Reactive.Linq;

Otherwise, nothing works and the application will not build. On Thursday, we will talk about some of the advanced features of Akavache.

As always, the code is available here, it should run on any Android app from Xamarin Studio.

comments powered by Disqus