Offline data caching

Mobile apps have several benefits over web apps, one of which is the ability to operate offline and maintain offline data. There are a couple of reasons why offline data is important to a mobile app. First of all, you cannot guarantee your app will always have a network connection and the ability to directly connect to live data; so, supporting "offline" allows users to use the app, even if only for limited use cases, when they are operating with limited or no connectivity. Second, users expect mobile apps to offer high performance, specifically quick access to data without having to wait. By maintaining an offline cache, an app can present a user with data immediately while it's busy retrieving a fresh data set, providing perceived performance to the user. It is important that when the cache updates, the user receives that updated data automatically so that they are always seeing the latest data possible, depending on specific use cases of course.

There are several ways of implementing a data cache in a mobile app, all depending on the size and complexity of the data that needs to be stored. In most cases, storing the cache in a local database using SQLite is the best approach.

In this chapter, we will update the TripLog app to maintain a cache of log entries and keep the cache in sync with the live API as data is received from Azure backend service. The data cache will be stored in a SQLite database, but to ease the implementation, we will use an open source library called Akavache. Akavache provides not only caching capabilities, but also a very easy to use API to update the cache to handle many different scenarios.

Note

For the purposes of this book and the TripLog sample application, we will only be using a small subset of the Akavache features. For a closer look at the Akavache library and all of its capabilities, check it out on GitHub at https://github.com/akavache/Akavache.

Adding the Akavache library

Like most libraries that we have used throughout this book, the Akavache library can be obtained via NuGet. First, add a reference to the library, including all of its dependencies to the core library project and the platform-specific projects.

Next, we need to add Akavache to our IoC container so that it can be injected into our ViewModels. Akavache comes with some static variables that make it very easy to use; however, we want to instead instantiate our own instance and add it to the IoC to maintain separation.

Bind<Akavache.IBlobCache> ().ToConstant (Akavache.BlobCache.LocalMachine);

Maintaining an offline data cache

Currently, the TripLog app's MainViewModel calls the TripLogApiDataService to get its data directly from the live API. As mentioned at the beginning of this chapter, in the event of little or no connectivity, the TripLog app will fail to display any log entries. With a few minor modifications to the MainViewModel, we can set it up to use the Akavache library to retrieve log entries from a local cache and also to refresh that cache with any changes in the data set once a connection with live API has succeeded.

First, update the MainViewModel constructor to require an instance of Akavache.IBlobCache, which will be injected via our Ninject implementation from Chapter 4, Platform Specific Services and Dependency Injection:

readonly IBlobCache _cache;

// ...

public MainViewModel (INavService navService,
   ITripLogDataService tripLogService,
   IBlobCache cache)
   : base (navService)
{
   _tripLogService = tripLogService;
   _cache = cache;

    LogEntries = new ObservableCollection<TripLogEntry> ();
}

Next, we need to modify the logic in the LoadEntries method to tie into the local offline cache. To do this, we will leverage an extension method in Akavache called GetAndFetchLatest. This method actually performs two functions. First, it immediately returns cached data, given a specific key (in our case, entries.) Second, it makes a call to the API based on the given Func and updates the cache for the given specific key. Because it is performing two functions, it will ultimately return twice. In order to handle this, and because it is returning an IObservable, we can use the ObservableExtensions Subscribe extension method to handle each return as it occurs. In the Subscribe extension method, we will update the LogEntries ObservableCollection property on the MainViewModel based on what is either returned from the cache or from the subsequent API call, if successful:

public override async Task Init ()
{
    LoadEntries ();
}

void LoadEntries()
{
    if (IsBusy)
        return;

    IsBusy = true;

    try
    {
        // Load from local cache and then immediately load from API
        _cache.GetAndFetchLatest("entries",
            async () => await _tripLogService.GetEntriesAsync())
            .Subscribe(entries => {
                LogEntries =
                new ObservableCollection<TripLogEntry>(entries);
            });
    }
    finally {
        IsBusy = false;
    }
}

Notice that, because we're calling GetAndFetchLatest and using the Subscribe method, the LoadEntries method is no longer async.

The first time the app is launched with this code, the cache will be populated. On any subsequent launches of the app, you should notice that data appears immediately as the view is constructed. If you add an item to the backend service database, and then launch the app again, you should see the new item fall into place after a couple of seconds.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.251.57