Question 3
Caching strategy
How do you design a caching strategy for network data (e.g. memory vs disk vs database), and how do you decide what to cache?
Answer outline
Memory (NSCache, in-process stores): fastest access — ideal for images, buffer, and data models for the current session.
Disk (files, URLCache): survives restarts; good for large blobs and structured data. Prefer versioned keys (URL + etag or schema version) so migrations are clear.
On disk, URLCache backs HTTP response caching for URLSession. You set memory/disk budgets and attach the cache to a URLSessionConfiguration.
let memoryCapacity = 50 * 1_024 * 1_024 // 50 MB RAM
let diskCapacity = 200 * 1_024 * 1_024 // 200 MB disk
let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first
let cache = URLCache(
memoryCapacity: memoryCapacity,
diskCapacity: diskCapacity,
directory: cacheDir
)
// setup the configuration with the cache
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
// setup the URLSession with the configuration
let session = URLSession(configuration: config)
// Responses may be served from cache per policy and Cache-Control headers
Database (Core Data, SwiftData, GRDB): best when you need queries, relationships, or incremental updates, not just key-value blobs. Treat the DB as the source of truth for offline with clear staleness rules.
Cache high-read, latency-sensitive resources. Should be content the user explicitly saves or revisits. Don’t cache highly sensitive data without encryption and Keychain where appropriate.
Principles
- Policy per resource — TTL, max size, invalidation on user action (logout), and etag(entity tag that represents a specific version)/version checks.
- Stale-while-revalidate for feeds: show cache immediately, refresh in background when online.
- Single writer to avoid races; (actors, serial queues).