How to Implement Image and GIF Caching for Mobile Fitness Apps: A Technical Guide

Implement a three-tier caching strategy—combining in-memory LRU caches, persistent disk storage, and HTTP cache-control headers—to deliver 180×180 exercise thumbnails and GIFs instantly while minimizing mobile data usage.

Mobile fitness applications displaying exercise libraries require instant access to thousands of visual assets. The hasaneyldrm/exercises-dataset repository provides 1,324 pre-scaled thumbnails and matching GIFs with deterministic URLs, making it an ideal dataset for implementing robust image and GIF caching in mobile fitness apps.

Three-Layer Caching Architecture

Implementing image and GIF caching for mobile fitness apps requires coordinating three distinct storage layers to eliminate network latency when displaying content from the images/ and videos/ directories.

Memory Cache Layer

The memory cache delivers fastest access for assets currently visible on screen. Implement platform-specific LRU caches—LruCache on Android, NSCache on iOS, or React Native FastImage's built-in memory cache—to store recently decoded bitmaps. This layer eliminates disk I/O overhead when users scroll through exercise lists populated from data/exercises.json.

Disk Cache Layer

The disk cache provides persistent storage across app launches, reducing repeated downloads of the repository's assets. Store downloaded files in the app's cache directory with size-limited eviction policies. Libraries like Glide (Android), Coil (Android/Kotlin), SDWebImage (iOS), and react-native-fast-image automatically manage disk caches using the repository's deterministic filenames (<id>-<media_id>.jpg).

Network Optimization Layer

Minimize payload by serving the repository's pre-scaled 180×180 assets with HTTP Cache-Control and ETag headers. Configure your CDN to return 304 Not Modified responses when assets haven't changed, leveraging the created_at timestamp in data/exercises.json for cache-busting strategies. Refer to setup.html in the repository for backend configuration examples.

Implementation Workflow

Follow this sequence when integrating with the hasaneyldrm/exercises-dataset repository to load metadata and cache assets efficiently.

Load Exercise Metadata

First, retrieve the exercise list from data/exercises.json. Each record contains image (thumbnail path) and gif_url (animation path) fields pointing to assets in the images/ and videos/ folders:

{
  "id": "0001",
  "name": "3/4 Sit-Up",
  "image": "images/0001-2gPfomN.jpg",
  "gif_url": "videos/0001-2gPfomN.gif",
  "created_at": "2023-01-15T10:30:00Z",
  "media_id": "2gPfomN"
}

Resolve full URLs by prepending your CDN base URL (e.g., https://cdn.example.com/) to these relative paths.

React Native Implementation

Use react-native-fast-image to automatically handle memory and disk caching with immutable cache control:

import FastImage from 'react-native-fast-image';

const CDN = 'https://cdn.example.com/';

export const ExerciseCard = ({ ex }) => {
  const thumbUrl = `${CDN}${ex.image}`;
  const gifUrl = `${CDN}${ex.gif_url}`;

  return (
    <FastImage
      style={{ width: 180, height: 180, borderRadius: 8 }}
      source={{
        uri: thumbUrl,
        priority: FastImage.priority.normal,
        cache: FastImage.cacheControl.immutable,
      }}
      placeholder={require('../assets/placeholder.png')}
      onLoadEnd={() => {
        FastImage.preload([{ uri: gifUrl }]);
      }}
    />
  );
};

Android Implementation with Coil

For Kotlin Android apps, implement Coil to cache thumbnails and preload GIFs from the repository:

import coil.load
import coil.request.CachePolicy
import coil.transform.CircleCropTransformation

fun loadExerciseImage(imageView: ImageView, exercise: Exercise) {
    val baseUrl = "https://cdn.example.com/"
    val thumbUrl = baseUrl + exercise.image
    val gifUrl = baseUrl + exercise.gif_url

    imageView.load(thumbUrl) {
        placeholder(R.drawable.placeholder)
        crossfade(true)
        diskCachePolicy(CachePolicy.ENABLED)
        memoryCachePolicy(CachePolicy.ENABLED)
    }

    imageView.context.imageLoader.enqueue(
        ImageRequest.Builder(imageView.context)
            .data(gifUrl)
            .diskCachePolicy(CachePolicy.ENABLED)
            .memoryCachePolicy(CachePolicy.ENABLED)
            .build()
    )
}

iOS Implementation with SDWebImage

Configure SDWebImage to cache assets from the repository's images/ and videos/ directories:

import SDWebImage

func setExerciseImage(_ imageView: UIImageView, exercise: Exercise) {
    let base = "https://cdn.example.com/"
    let thumbURL = URL(string: base + exercise.image)!
    let gifURL = URL(string: base + exercise.gif_url)!

    imageView.sd_setImage(
        with: thumbURL,
        placeholderImage: UIImage(named: "placeholder"),
        options: [.cacheMemoryOnly, .refreshCached]
    )

    SDWebImagePrefetcher.shared.prefetchURLs([gifURL])
}

Flutter Implementation

Use cached_network_image to cache 180×180 thumbnails from the dataset:

import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';

class ExerciseTile extends StatelessWidget {
  final Map ex;
  const ExerciseTile(this.ex, {Key? key}) : super(key: key);
  static const cdn = 'https://cdn.example.com/';

  @override
  Widget build(BuildContext context) {
    final thumb = cdn + ex['image'];
    final gif = cdn + ex['gif_url'];

    return CachedNetworkImage(
      imageUrl: thumb,
      placeholder: (_, __) => const SizedBox(
        width: 180,
        height: 180,
        child: Center(child: CircularProgressIndicator()),
      ),
      imageBuilder: (_, img) => Image(image: img, width: 180, height: 180),
      fadeOutDuration: const Duration(milliseconds: 300),
      fadeInDuration: const Duration(milliseconds: 300),
    );
  }
}

Cache Invalidation Strategies

When updating exercise content from hasaneyldrm/exercises-dataset, implement cache invalidation using the deterministic filename structure. Since assets follow the pattern <id>-<media_id>.jpg and <id>-<media_id>.gif as defined in data/exercises.json, updating the media_id or created_at fields automatically busts existing caches by generating new URLs.

Validate incoming data against data/exercises.schema.json before caching to ensure compatibility. For manual cache clearing, purge disk caches when releasing major app updates or when the README.md indicates breaking changes to the asset structure.

Summary

  • Three-tier architecture: Combine memory LRU caches, persistent disk storage in the app's cache directory, and HTTP cache-control headers for instant 180×180 thumbnail display.
  • Deterministic URLs: Leverage the repository's <id>-<media_id> naming convention in images/ and videos/ directories for reliable cache keys that auto-bust when content updates.
  • Progressive loading: Display static thumbnails immediately while preloading GIFs from the videos/ folder in the background to ensure smooth animations.
  • Schema validation: Use data/exercises.schema.json to validate metadata before caching, ensuring field integrity for image and gif_url paths.

Frequently Asked Questions

How much storage should I allocate for caching the exercises dataset?

Allocate 50-100MB for LRU disk caching when using the hasaneyldrm/exercises-dataset. With 1,324 exercises averaging 10-15KB per thumbnail and 200-500KB per GIF, a complete local cache requires approximately 300-700MB. Configure your caching library to evict least-recently-used assets when approaching these limits, prioritizing retention of frequently accessed items from the images/ directory.

Should GIFs be cached differently than static thumbnails?

Yes. Cache static thumbnails (from the repository's images/ directory) aggressively in both memory and disk since they appear frequently in exercise lists. Cache GIFs (from the videos/ directory) primarily on disk due to their larger file size, keeping only the currently playing animation in memory to prevent memory pressure during workouts.

How do I handle cache updates when the dataset changes?

Monitor the media_id or created_at fields in data/exercises.json. When either value changes, the <id>-<media_id>.jpg filename changes automatically, treating the asset as new content that bypasses existing cache entries. Configure your backend to serve appropriate HTTP headers based on the created_at timestamp to enable 304 Not Modified validation for unchanged assets.

Can I use this caching strategy with offline-first fitness apps?

Absolutely. Pre-populate the disk cache by downloading all 1,324 assets referenced in data/exercises.json during app installation or when Wi-Fi is available. Store files from the images/ and videos/ directories in persistent storage rather than temporary cache directories, ensuring exercise demonstrations remain accessible without network connectivity.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →