Skip to content

Caching Overview

Matthew Logan edited this page Feb 5, 2025 · 1 revision

Application Caching

InvasivesBC Caches data for the end user in two ways: using SQLite on the installed device, and using LocalForage in the development context to streamline cache functionality while bypassing the slowdown of the iOS build process.

Important

Each caching tool uses a Singleton design pattern.

Table of Contents

Base Cache Service

All locally stored cached data in InvasivesBC is extended from the generic BaseCacheService<T,T,T,T>. This is to ensure overall consistency and functionality while more datasets are added overtime. The Base Cache Service declares four <T> types

  • Repo Metadata: All Details contained by a Metadata Entry
  • RepositoryDownloadRequestSpec: Information details for a Download e.g. API Url, Cache Targets, bounding boxes
  • ProgressCallbackParams: Details for Callback Functionality, used for Progress bars, updates
  • RepositoryStatusSchema: Enum for Cache Statuses, e.g. READY, CACHED, ERROR, etc

Folder Hierarchies

Cache files are stored in utils organized by folders. Each folder requires the following four files to operate:

  1. context.ts
  2. index.ts
  3. localforage-cache.ts
  4. sqlite-cache.ts
---
Note: M
---

graph BT;
  LocalForageWellCacheService -- Implements --> CC(abstract WellCacheService)
  SQLiteWellCacheService -- Implements --> CC
  CC -- Extends --> BCS(abstract BaseCacheService)
  SF(WellCacheServiceFactory) -- Returns --> SQLiteWellCacheService
  SF(WellCacheServiceFactory.getPlatformInstance) -- Returns --> LocalForageWellCacheService
Loading

Context

Context is the delegator. Given the current environment configuration (Android, IOS, Web), It's primary duty is to return the correct service being used for caching.

// Example, Use the SQLite for Mobile Devices, use LocalForage for rest
class WellCacheServiceFactory {
  static async getPlatformInstance(): Promise<WellCacheService> {
    if ([Platform.IOS, Platform.ANDROID].includes(PLATFORM)) {
      return SQLiteWellCacheService.getInstance();
    }
    return await LocalForageWellCacheService.getInstance();
  }
}

export default WellCacheServiceFactory;

Index

The Generic BaseCacheService is extended by the class defined in index.ts. Interfaces are defined for the BaseCacheService as well as common methods needed for consistent cache operations. Method declarations here add type guards to ensure consistency in the development between development and release builds of the application.

LocalForage

LocalForage is used for development builds and instantiates the class defined in index.ts. It interacts with the browser's client-side IndexedDB. This improves the development of cache-related features by leveraging IndexedDB and the hot-reload environment of npm run start:mobileMode, reducing the time spent on iOS build processes.

Limitations

  • Keys must be stored as strings, requiring conversion to maintain consistency in places like IAPP records, where the Site ID is an integer
  • LocalForage provdes a high-level interface to IndexedDB, and its key-value syntax is more similar to working with localStorage, but with better data integrity.
  • There are no columns for key-value storage, requiring parsing of objects to extract necessary information

SQLite

sqlite-cache.ts is used by the Mobile deployments leveraging the Capacitor/SQLite Packages.

Limitations

  • Limited Datatypes. Objects are stringified and parsed on the way out.

Common Issues / FAQ

Q: How do check my databases to ensure the data is there?

A.

IndexDB:
Open DevTools in browser -> Storage -> IndexedDB -> http:localhost:3000 -> localforage(default) -> Select your cache

SQLite

  1. In XCode build and start the application.
  2. Note the location the Cache in the Logs, and open that location in your terminal.
  3. run sqlite3 (Installed by default on MacOS)
  4. run .open <NAME OF DB>

This allows read-only access to the device DB so you can ensure things are correct. If things go wrong during development (bad migrations, entries not deleting as expected), you can delete the db file and it will be recreated next time you run the app.