Skip to content
intermediate Phase 5 · Networking & Data

Caching & Offline Support

Implement URLCache, custom caching layers, and offline-first data strategies.

45m
2 problems
Topic Progress 0%

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?

Question 1 options

2. What cache policy always fetches fresh data?

Question 2 options

3. Why use NSCache over NSDictionary for caching?

Question 3 options

4. What is the cache-then-network pattern?

Question 4 options

Flashcards

Question

What is URLCache?

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?

Answer

.returnCacheDataElseLoad uses cache if available. .reloadIgnoringLocalCacheData always fetches fresh data.

Question

What is an offline-first app?

Answer

An app that shows locally cached data immediately and syncs with the server when connectivity is available.

Question

What is NSCache?

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.