URLCache
What is URLCache?
URLCache is Apple's built-in HTTP caching system. It stores responses to URL requests in memory and on disk, automatically serving cached responses when appropriate.
Default Cache Configuration
URLCache.shared is used by default with URLSession.shared:
let cache = URLCache.shared
print("Memory capacity: \(cache.memoryCapacity / 1024 / 1024) MB")
print("Disk capacity: \(cache.diskCapacity / 1024 / 1024) MB")
Custom Cache Size
Configure cache for your app's needs:
let cache = URLCache(
memoryCapacity: 50 * 1024 * 1024,
diskCapacity: 200 * 1024 * 1024,
diskPath: "network-cache"
)
let config = URLSessionConfiguration.default
config.urlCache = cache
config.requestCachePolicy = .returnCacheDataElseLoad
let session = URLSession(configuration: config)
Cache Policies
Control when cached data is used:
.returnCacheDataElseLoad- Use cache if available, otherwise fetch.reloadIgnoringLocalCacheData- Always fetch, ignore cache.returnCacheDataAndLoadElseLoad- Use cache, fetch in background.returnCacheDataDontLoad- Only use cache, never fetch
Clearing Cache
URLCache.shared.removeAllCachedResponses()
Custom Caching Layer
Why Custom Caching?
URLCache handles HTTP caching, but sometimes you need more control. A custom caching layer lets you cache decoded objects and manage eviction policies.
NSCache-Based Cache
NSCache is a thread-safe, memory-efficient cache:
class Cache<Key: Hashable, Value> {
private let wrapped = NSCache<WrappedKey, Entry>()
private let dateProvider: () -> Date
private let entryLifetime: TimeInterval
init(maximumEntryCount: Int = 100, entryLifetime: TimeInterval = 12 * 60 * 60) {
self.dateProvider = Date.init
self.entryLifetime = entryLifetime
wrapped.countLimit = maximumEntryCount
}
func insert(_ value: Value, forKey key: Key) {
let entry = Entry(value: value, expirationDate: dateProvider().addingTimeInterval(entryLifetime))
wrapped.setObject(entry, forKey: WrappedKey(key))
}
func value(forKey key: Key) -> Value? {
guard let entry = wrapped.object(forKey: WrappedKey(key)) else { return nil }
guard dateProvider() < entry.expirationDate else {
removeValue(forKey: key)
return nil
}
return entry.value
}
func removeValue(forKey key: Key) {
wrapped.removeObject(forKey: WrappedKey(key))
}
}
Disk Cache
For persistent caching across app launches, use FileManager to write data to the caches directory.
Network Cache Pattern
Combine memory and disk caching: check memory first, then disk, then network. Save responses to both layers.
Offline-First Strategies
What is Offline-First?
Offline-first apps prioritize showing locally cached data immediately, then syncing with the server when connectivity is available.
Cache-Then-Network Pattern
Show cached data first, then update from the network:
class DataRepository<T: Codable> {
private let cacheKey: String
private let apiEndpoint: URL
func fetch() async throws -> T {
if let cached = UserDefaults.standard.data(forKey: cacheKey),
let decoded = try? JSONDecoder().decode(T.self, from: cached) {
Task { await refreshFromNetwork() }
return decoded
}
return try await refreshFromNetwork()
}
private func refreshFromNetwork() async throws -> T {
let (data, _) = try await URLSession.shared.data(from: apiEndpoint)
UserDefaults.standard.set(data, forKey: cacheKey)
return try JSONDecoder().decode(T.self, from: data)
}
}
Offline Queue
Queue requests made while offline and process them when connectivity returns:
class OfflineQueue {
private var pendingRequests: [PendingRequest] = []
private let monitor = NWPathMonitor()
init() {
monitor.pathUpdateHandler = { [weak self] path in
if path.status == .satisfied {
self?.processQueue()
}
}
monitor.start(queue: DispatchQueue(label: "OfflineQueue"))
}
func enqueue(_ request: PendingRequest) {
pendingRequests.append(request)
saveToDisk()
}
private func processQueue() {
for request in pendingRequests {
Task { try? await execute(request) }
}
pendingRequests.removeAll()
}
}
Network Connectivity Check
Use NWPathMonitor to detect connectivity changes and trigger sync.
Quiz
1. What does URLCache.shared provide?
2. What cache policy always fetches fresh data?
3. Why use NSCache over NSDictionary for caching?
4. What is the cache-then-network pattern?
Flashcards
Question
What is URLCache?
Click to reveal answer
Answer
Apple's built-in HTTP caching system that stores URL responses in memory and on disk.
Question
What is the difference between .returnCacheDataElseLoad and .reloadIgnoringLocalCacheData?
Click to reveal answer
Answer
.returnCacheDataElseLoad uses cache if available. .reloadIgnoringLocalCacheData always fetches fresh data.
Question
What is an offline-first app?
Click to reveal answer
Answer
An app that shows locally cached data immediately and syncs with the server when connectivity is available.
Question
What is NSCache?
Click to reveal answer
Answer
A thread-safe, memory-efficient cache that automatically evicts entries under memory pressure.
Revision Notes
Key Takeaways
- 1. URLCache provides automatic HTTP response caching for URLSession
- 2. NSCache is thread-safe and handles memory pressure automatically
- 3. Cache-then-network pattern provides fast UX with fresh data
- 4. NWPathMonitor detects connectivity for offline-first strategies
Interview Tips
- • Explain URLCache policies and when to use each
- • Describe NSCache vs NSDictionary for caching
- • Discuss offline-first architecture patterns
- • Know how to implement request queuing for offline support
Cheat Sheet
URLCache: Built-in HTTP cache with .returnCacheDataElseLoad. NSCache: Thread-safe, memory-warning-aware in-memory cache. DiskCache: FileManager-based persistence. Offline Queue: Store requests, process when online. NWPathMonitor: Detect connectivity. Cache-Then-Network: Show cached data, update in background.