HTTP Cache Generator
Generate a production HTTP caching layer that integrates with your existing networking code. Supports Cache-Control directives, ETag/Last-Modified conditional requests, stale-while-revalidate, and offline fallback.
When This Skill Activates
Use this skill when the user:
- Asks to "add HTTP caching" or "cache API responses"
- Wants "offline support" or "offline fallback"
- Mentions "reduce API calls" or "cache network responses"
- Asks about "ETag" or "conditional requests" or "304 Not Modified"
- Wants "stale-while-revalidate" behavior
Pre-Generation Checks
1. Project Context Detection
- [ ] Check Swift version (requires Swift 5.9+)
- [ ] Check deployment target (iOS 16+ / macOS 13+)
- [ ] Search for existing caching implementations
- [ ] Identify source file locations
2. Networking Layer Detection
Search for existing networking code:
Glob: **/*API*.swift, **/*Client*.swift, **/*Network*.swift
Grep: "APIClient" or "URLSession" or "HTTPURLResponse"
If networking-layer generator was used, detect the APIClient protocol and generate a decorator that wraps it.
3. Conflict Detection
Search for existing caching:
Glob: **/*Cache*.swift
Grep: "URLCache" or "ResponseCache" or "CachePolicy"
If found, ask user whether to replace or extend.
Configuration Questions
Ask user via AskUserQuestion:
-
Cache storage sizes?
- Small (10 MB memory / 50 MB disk)
- Medium (25 MB memory / 100 MB disk) — recommended
- Large (50 MB memory / 250 MB disk)
-
Caching strategy?
- Respect server Cache-Control headers (standard)
- Cache-first with background revalidation (stale-while-revalidate)
- Manual per-endpoint policies
-
Offline support?
- Yes — serve stale cache when network unavailable
- No — only cache while online
-
Integration style?
- Decorator wrapping existing APIClient (recommended if networking-layer exists)
- Standalone cache you call directly
Generation Process
Step 1: Read Templates
Read http-cache-patterns.md for architecture guidance.
Read templates.md for production Swift code.
Step 2: Create Core Files
Generate these files:
HTTPCacheConfiguration.swift— Memory/disk sizes, default policyCachePolicy.swift— Per-endpoint enum (default, noCache, forceCache, cacheFirst)CacheControlHeader.swift— Cache-Control header parserConditionalRequestHandler.swift— ETag/Last-Modified/304 handlingHTTPResponseCache.swift— Protocol + disk-backed response store
Step 3: Create Integration Files
Based on configuration:
CachingAPIClient.swift— Decorator wrapping existing APIClient (if decorator style)NetworkReachability.swift— NWPathMonitor wrapper (if offline support selected)
Step 4: Determine File Location
Check project structure:
- If
Sources/Networking/exists →Sources/Networking/Cache/ - If
App/Networking/exists →App/Networking/Cache/ - If
Networking/exists →Networking/Cache/ - Otherwise →
Cache/
Output Format
After generation, provide:
Files Created
Networking/Cache/
├── HTTPCacheConfiguration.swift # Memory/disk sizes, default policy
├── CachePolicy.swift # Per-endpoint caching enum
├── CacheControlHeader.swift # Cache-Control header parser
├── ConditionalRequestHandler.swift # ETag/Last-Modified/304
├── HTTPResponseCache.swift # Protocol + disk implementation
├── CachingAPIClient.swift # Decorator for existing APIClient
└── NetworkReachability.swift # NWPathMonitor (optional)
Integration Steps
Wrap your existing client:
let baseClient = URLSessionAPIClient(configuration: .production)
let cachingClient = CachingAPIClient(
wrapping: baseClient,
cache: DiskHTTPResponseCache(),
configuration: .default
)
// Use cachingClient everywhere you used baseClient
let users = try await cachingClient.request(UsersEndpoint())
Per-endpoint cache policy:
struct UsersEndpoint: APIEndpoint, CacheConfigurable {
var cachePolicy: CachePolicy { .cacheFirst(maxAge: 300) }
}
struct OrdersEndpoint: APIEndpoint, CacheConfigurable {
var cachePolicy: CachePolicy { .noCache }
}
With SwiftUI:
struct UsersView: View {
@Environment(\.apiClient) private var apiClient
var body: some View {
List(users) { user in
Text(user.name)
}
.task {
// Automatically uses cache if available
users = try await apiClient.request(UsersEndpoint())
}
}
}
Testing
@Test
func cachedResponseReturnedOnSecondRequest() async throws {
let mockClient = MockAPIClient()
let cache = InMemoryHTTPResponseCache()
let cachingClient = CachingAPIClient(wrapping: mockClient, cache: cache)
mockClient.mockResponse(for: UsersEndpoint.self, response: [.mock])
// First request hits network
let first = try await cachingClient.request(UsersEndpoint())
#expect(mockClient.requestCount == 1)
// Second request comes from cache
let second = try await cachingClient.request(UsersEndpoint())
#expect(mockClient.requestCount == 1) // No additional network call
#expect(first == second)
}
References
- http-cache-patterns.md — Cache-Control directives, ETag flow, stale-while-revalidate
- templates.md — All production Swift templates
- Related:
generators/networking-layer— Base networking layer this decorates