pub package codecov

Motivation

Imagine a user launching the app for the second time and seeing a skeletal loading screen or progress bar, especially in parts of the UI where the data doesn't change often. This is not a good UX.

To fix this, we can load the data from the cache in the first step, and then update the data in the second step.

We don't aim to minimize requests to the server, but to minimize the time it takes to display the data to the user.

Using

import 'package:http/http.dart' as http;
import 'package:json_fetcher/json_fetcher.dart';
import 'package:path_provider/path_provider.dart';

Future<String> get cachePath => getApplicationCacheDirectory().then((dir) => dir.path);
final client = JsonHttpClient(http.Client(), createCache(cachePath));
final postsStream = JsonFetcher<Model>>(
  client,
  (json) => Model.fromJson(json),
).fetch('https://example.com/get-json');

Configuration

At the first step you should create JsonHttpClient:

final jsonClient = JsonHttpClient(httpClient, cache);

HttpClient

This package uses standard Dart http package.

Basicaly, is enough to use Client() for creating raw client:

final jsonClient = JsonHttpClient(Client(), cache);

So, you can configure client more precisely before creating JsonHttpClient itself.

Client httpClient() {
  if (Platform.isAndroid) {
    final engine = CronetEngine.build(
        cacheMode: CacheMode.memory,
        cacheMaxSize: 1000000);
    return CronetClient.fromCronetEngine(engine);
  }
  if (Platform.isIOS || Platform.isMacOS) {
    final config = URLSessionConfiguration.ephemeralSessionConfiguration()
      ..cache = URLCache.withCapacity(memoryCapacity: 1000000);
    return CupertinoClient.fromSessionConfiguration(config);
  }
  return IOClient();
}

final jsonClient = JsonHttpClient(httpClient(), cache);

Tip

The package contains convenitent client-wrapper with logging capabitility LoggableHttpClient, see Flutter example as use case.

Cache

For creating cache just use convenient function createCache(path).

Note

path can be String of Future<String>

Future<String> variant is very useful for creating a client somewhere in DI at app startup, to prevent unneded waiting.

In Flutter Android/iOS app you can create client that caches data in standard cache directory with following snippet:

import 'package:path_provider/path_provider.dart';

Future<String> get path => getApplicationCacheDirectory().then((value) => value.path);

final jsonClient = JsonHttpClient(httpClient(), createCache(path));

Tip

Package exposes interface HttpCache, that can be implemented to use your own cache.

How it works

If the data has changed (new data has arrived from the network), it will be updated in the second step. That's why we use Stream<T> instead of Future<T>. The stream's subscriber will get two snapshots.

If the data hasn't changed, the stream's subscriber will get one snapshot

If there is no cached copy, the stream's subscriber will get one snapshot.

Cache

The cache data is managed by implementations of HttpCache.

dart:io (mobile/desktop)

HttpFilesCache stores data in files. Performance improved by using concurrent IO with synchronization by keys.

Web

HttpWebCache stores data in IndexedDB. It uses package:web from Dart team.