In this post we will be looking at a library called Akavache by Paul Betts. This library lets you store key/value paired data easily. You can store any arbitrary data like user settings, but it can also be used to store cached data that will expire over time. We will see what Akavache does and how we can use it to create a better user experience.
What Akavache can do for you
Almost every app I build get information from the internet, or at least a remote API server. In order to comply to the app store rules, and even more important, to create a great user experience, this fetching of data needs to be asynchronous. To create an even better user experience, it helps to have data there already when the user navigates and then update it silently. Have you ever taken a good look at the Facebook app? Or LinkedIn? Or your favorite news app? Whenever you start the app, they do not show you a blank loading screen. They show you the content you may have viewed before, and then load the fresh content in the background, replacing it as you go.
Another scenario is when your app is used offline. It does not look very pretty when suddenly all your screens will come up empty. And maybe even the user expects that he/she can access the data when no internet is available.
This is what I use Akavache mainly for, but wait! There is more! It can also get URLs and images from the web and store them for you, or save user settings and even credentials in a secure way. But for caching it is very simple to use. You can just feed JSON objects to it, let Akavache know under what name to store it and you’re done.
Please note that you can use Akavache with a number of platforms and Xamarin is only one of them. You can also use it in desktop apps with WPF and Universal Windows app for instance. Of course, in this post I will demonstrate by using a Xamarin app.
How it works
Let us have a look at how we can use all this awesomeness.
The first thing we need to do is install the NuGet package. If you’re using Xamarin.Forms, like I will do, please do not forget to install the NuGet package in all of your projects and not just your shared library. Underneath the hood Akavache uses SQLite3 and because of this, there is also some platform-specific logic necessary in order to make it work.
After you have done this, we are almost ready to save data. Depending on what type of data you are going to save, this can be done on various locations. Underneath you will find a list of these locations with a small description.
- BlobCache.LocalMachine – General cached data. Retrieved objects from a server, i.e. news items, list of users, etc.
- BlobCache.UserAccount – Settings that the user does within your app. Some platforms sync this to the cloud.
- BlobCache.Secure – If you are saving sensitive data, this is the place where you want to put it.
- BlobCache.InMemory – Like the name says; it holds the data in-memory, whenever your app is killed, so is the data in there.
There is a little more to some of these platforms, mainly: for Xamarin.Android and Xamarin.iOS the LocalMachine cache may be cleaned when the device is running out of diskspace. And for Windows 10 and Xamarin.iOS the UserAccount and Secure locations will be synced to a cloud location.
The last thing we need to do to get crackin’, is setting the ApplicationName so the saved goods are tied to our application. This can easily be done by setting the BlobCache.ApplicationName property preferably in your app startup class.
Implementing them codez
Let me show you how to implement a basic usage of Akavache. All the code can be found here.
The first thing I have set up to give it a bit of body is a page with a ListView. In here we will show a list of JSON results we will get from a API backend, but in between we will put some caching.
The user interface
Underneath you can find the XAML I’ve used to build my page, although it isn’t very exciting.
Loading the data
I have chosen to use my shiny new have i been pwned? (HIBP) API wrapper to communicate with the awesome API created by Troy Hunt. With this website (and API) you can get a list of companies and websites that were compromised and check if your data was among it. These are called Breaches.
In the code underneath, you will see some of the basic plumbing needed to retrieve this data.
I create the ObservableCollections which will hold the breaches. I’ve introduced a Command that will retrieve the data for me. And lastly, I initialize the HIBP client to retrieve the actual data.
One thing that might stand out is that I use a ObservableRangeCollection, which is not in the Xamarin.Forms toolkit by default. This is a library by James Montemagno, called MvvmHelpers. It has some supporting methods that come in handy when using the MVVM pattern. If you want to read more on using the MVVM pattern in your apps, you can see my post on this here.
Caching the data and retrieving from cache
The actual loading of the data involves the code below.
The LoadBreaches method doesn’t do very much in itself. Here we can influence the UI by setting some properties, in this case the IsLoading, to let the user know we are loading data. You can see it is directly hooked up in the RefreshCommand and is getting data from the GetBreaches method.
The GetBreaches method is where the actual magic happens. First thing you see is that I access the BlobCache.LocalMachine. Since it’s data that I can easily load again if I have to, this is no problem. Then, I tell the cache object, to retrieve the latest from a collection which I named “breaches”. The second parameter takes in a method that retrieves the data from its original source, in our case the HIBP API. The offset parameter is a method that determines whether or not the data in our cache should be invalidated.
The login in here is totally up to you. I have implemented two conditions: either the user can force it, or the data is more than 24 hours old. Again, how you let the user force a reload is up to you. I force a reload whenever the user triggers the PullToRefresh on my ListView.
Using IObservable to be notified of new data
When you look into the return types of Akavache, you will see that it returns a lot of IObservable types. With these types, you can use the Subscribe method. With this method, you tell the class that implements the IObservable interface, that you want to receive notifications. In this case, we get notified whenever new data needs to be loaded from the remote source. We will receive the new data as the parameter in the Subscribe delegate. Finally, I use the ReplaceRange method from the MvvmHelpers, to replace the range within the collection currently shown to the user.
This way, the collection gets updated without the user really noticing.
In a real-life scenario, you probably want to move some of this code to a separate service class which handles all the loading of your data. Your PageModel has no business of knowing where the data is coming from exactly. But this will hopefully give you an idea of how to work with this great library.
Keeping your data intact
To keep your data intact, you should close the cache. You should do so by simpling calling BlobCache.Shutdown().Wait(); whenever your application closes.
For mobile apps this method seems unsuitable, also according to Paul Betts himself. Mainly, because an app should be prepared to be killed at any time. Depending on the platform the app won’t even be notified that they are going to be shut down. So, how do we solve this?
In the Github issue I have referenced above, a pretty good solution is coined. Be sure to check it out here. In short, it involves wrapping the cache in a repository class and get your data through there. You can then also include a shutdown method in your repository class which is called in your OnSleep method (for Xamarin.Forms, that is).
This library could easily make it to the list of NuGets which are in my every project. It is very easy to use, unifies the way to collect and store data and makes for a better user experience in connected apps scenarios.
In this post only a small fraction of what is possible with Akavache is shown. For a more complete overview, please have a look at the project’s page on Github.
The full code for this post can be found on my Github page.