Skip to main content

Overview

ThumbnailCache provides a two-tier caching system for R2 object thumbnails. It combines in-memory caching with disk persistence and implements request coalescing to prevent duplicate fetches. The actor-based design ensures thread-safe access from any context.
Located at: Fiaxe/Services/ThumbnailCache.swift:6

Type Definition

Implemented as an actor for thread-safe concurrent access. All methods can be called from any isolation context.

Singleton Instance

Access the shared cache instance.

Methods

thumbnail()

Retrieves or generates a thumbnail for an R2 object key.
String
required
The R2 object key to generate a thumbnail for (e.g., "photos/image.jpg").
R2Credentials
required
R2 credentials used to generate presigned URLs for fetching the image.
Returns: Thumbnail image (120×120 max), or nil if generation fails.

Implementation Details

The method implements a three-tier lookup:
  1. Memory cache - Instant return if cached in NSCache
  2. Disk cache - Load from disk if available, promote to memory
  3. Network fetch - Generate thumbnail from R2, cache in both memory and disk
Request CoalescingIf multiple requests for the same thumbnail arrive simultaneously, only one network request is made. Other requests await the same task result.

clearMemory()

Clears the entire in-memory cache.
Useful for freeing memory during low-memory conditions. Disk cache is preserved.

Cache Configuration

The cache is configured with size limits:
Int
Maximum number of images in memory cache (500).
Int
Maximum memory footprint in bytes (100 MB).
NSCache automatically evicts least-recently-used items when limits are exceeded.

Disk Cache

Thumbnails are persisted to the user’s cache directory:
Disk cache location:

File Naming

Cache keys are sanitized for safe filenames:
Examples:
  • photos/vacation.jpgphotos_vacation.jpg.png
  • bucket:folder/file.pngbucket_folder_file.png.png

Storage Format

All thumbnails are stored as PNG:
PNG format provides lossless compression suitable for thumbnails.

Thumbnail Generation

Image Thumbnails

Generated by downloading and resizing:

Video Thumbnails

Generated using AVFoundation:
Extracts the first frame from video files.

Supported Images

JPEG, PNG, GIF, HEIC, BMP, TIFF, WebP

Supported Videos

MP4, MOV, AVI, MKV, WebM, M4V

Resize Algorithm

The cache maintains aspect ratio while fitting within 120×120:
Examples:
  • 1920×1080 → 120×67.5 (fits width)
  • 800×1200 → 80×120 (fits height)
  • 100×100 → 100×100 (no upscaling)

Request Coalescing

Prevents duplicate fetches for the same thumbnail:
If 10 views request the same thumbnail simultaneously:
  • Only 1 network request is made
  • All 10 requests await the same task
  • All receive the same result

Bucket Scoping

Cache keys are scoped by bucket name:
This ensures:
  • Different buckets with same key names don’t share thumbnails
  • Switching between buckets doesn’t show wrong thumbnails
  • Cache hits are always bucket-specific

Usage Example

Performance Characteristics

NSCache automatically evicts items under memory pressure. The 100 MB limit prevents excessive memory usage.
PNG compression reduces disk space. A 120×120 thumbnail typically uses 10-50 KB.
Actor isolation ensures safe concurrent access. Multiple views can request thumbnails simultaneously without race conditions.
Request coalescing prevents duplicate downloads. Once cached, thumbnails load instantly.

Cache Persistence

Disk Cache LifetimeThe disk cache persists across app launches and remains until:
  • User manually clears cache (if implemented)
  • macOS automatically clears ~/Library/Caches during cleanup
  • App is uninstalled
Memory cache is cleared on every app launch.

Error Handling

The cache gracefully handles errors:
Failure cases:
  • Invalid presigned URL
  • Network errors
  • Unsupported file formats
  • Corrupted images
  • Disk write failures
All failures return nil rather than throwing errors, allowing UI to show fallback icons.

Thread Safety

Actor IsolationThe actor keyword ensures all cache operations are serialized:
  • No race conditions on inFlight dictionary
  • Safe NSCache access (though NSCache is already thread-safe)
  • Predictable behavior under concurrent load