All skills

Code generator skills that produce production-ready Swift code for common app components. Use when user wants to add logging, analytics, onboarding, review prompts, networking, authentication, paywalls, settings, persistence, error monitoring, CI/CD pipelines, localization, push notifications, deep linking, testing, accessibility, widgets, feature flags, app icons, image caching, pagination, HTTP caching, share cards, social export, subscription lifecycle, referral systems, watermarks, streak tracking, milestone celebrations, what's new screens, lapsed user re-engagement, usage insights, variable rewards, consent flows, account deletion, permission priming, force updates, state restoration, debug menus, offline queues, feedback forms, announcement banners, quick win sessions, Spotlight indexing, App Clips, screenshot automation, background processing, app extensions, data export, or SwiftUI preview sample data and variant matrices.

Use this Skill: https://skilld.dev/gh/rshankras/claude-code-apple-skills/generators

This session only. Nothing lands on disk.

http-cachetemplates.md

≈4.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

HTTP Cache Code Templates

Production-ready Swift templates for HTTP response caching. All code targets iOS 16+ / macOS 13+ and uses modern Swift concurrency.

CachePolicy.swift

import Foundation

/// Defines how an individual endpoint's responses should be cached.
enum CachePolicy: Sendable {
    /// Use default behavior: respect server Cache-Control headers.
    case `default`

    /// Never cache this endpoint's responses.
    case noCache

    /// Always use cache if available, regardless of age.
    /// Falls back to network only on cache miss.
    case forceCache

    /// Return cache immediately, revalidate in background.
    /// `maxAge` controls how long the cached response is considered fresh.
    case cacheFirst(maxAge: TimeInterval = 300)

    /// Custom max-age override, ignoring server headers.
    case custom(maxAge: TimeInterval)
}

/// Conform endpoints to this protocol to specify per-endpoint cache behavior.
protocol CacheConfigurable {
    var cachePolicy: CachePolicy { get }
}

HTTPCacheConfiguration.swift

import Foundation

/// Global configuration for the HTTP cache layer.
struct HTTPCacheConfiguration: Sendable {
    /// Maximum memory cache size in bytes.
    let memoryCapacity: Int

    /// Maximum disk cache size in bytes.
    let diskCapacity: Int

    /// Default TTL when server provides no Cache-Control header.
    let defaultMaxAge: TimeInterval

    /// Whether to serve stale cache when offline.
    let offlineFallbackEnabled: Bool

    /// Default cache policy for endpoints that don't specify one.
    let defaultPolicy: CachePolicy

    static let small = HTTPCacheConfiguration(
        memoryCapacity: 10 * 1024 * 1024,   // 10 MB
        diskCapacity: 50 * 1024 * 1024,      // 50 MB
        defaultMaxAge: 300,
        offlineFallbackEnabled: true,
        defaultPolicy: .default
    )

    static let medium = HTTPCacheConfiguration(
        memoryCapacity: 25 * 1024 * 1024,   // 25 MB
        diskCapacity: 100 * 1024 * 1024,    // 100 MB
        defaultMaxAge: 300,
        offlineFallbackEnabled: true,
        defaultPolicy: .default
    )

    static let large = HTTPCacheConfiguration(
        memoryCapacity: 50 * 1024 * 1024,   // 50 MB
        diskCapacity: 250 * 1024 * 1024,    // 250 MB
        defaultMaxAge: 300,
        offlineFallbackEnabled: true,
        defaultPolicy: .default
    )

    static let `default` = medium
}

CacheControlHeader.swift

import Foundation

/// Parsed representation of a Cache-Control HTTP header.
struct CacheControlDirectives: Sendable {
    var maxAge: TimeInterval?
    var staleWhileRevalidate: TimeInterval?
    var noCache: Bool = false
    var noStore: Bool = false
    var mustRevalidate: Bool = false
    var isPublic: Bool = false
    var isPrivate: Bool = false
    var immutable: Bool = false

    /// Whether this response is cacheable at all.
    var isCacheable: Bool {
        !noStore
    }

    /// Parse a Cache-Control header value string.
    static func parse(_ headerValue: String) -> CacheControlDirectives {
        var directives = CacheControlDirectives()

        let parts = headerValue
            .split(separator: ",")
            .map { $0.trimmingCharacters(in: .whitespaces).lowercased() }

        for part in parts {
            if part == "no-cache" {
                directives.noCache = true
            } else if part == "no-store" {
                directives.noStore = true
            } else if part == "must-revalidate" {
                directives.mustRevalidate = true
            } else if part == "public" {
                directives.isPublic = true
            } else if part == "private" {
                directives.isPrivate = true
            } else if part == "immutable" {
                directives.immutable = true
            } else if part.hasPrefix("max-age=") {
                let value = part.dropFirst("max-age=".count)
                directives.maxAge = TimeInterval(value)
            } else if part.hasPrefix("stale-while-revalidate=") {
                let value = part.dropFirst("stale-while-revalidate=".count)
                directives.staleWhileRevalidate = TimeInterval(value)
            }
        }

        return directives
    }
}

ConditionalRequestHandler.swift

import Foundation

/// Handles ETag and Last-Modified conditional request headers.
struct ConditionalRequestHandler: Sendable {

    /// Add conditional headers to a request based on cached response metadata.
    static func addConditionalHeaders(
        to request: inout URLRequest,
        cachedEntry: CachedResponseEntry
    ) {
        if let etag = cachedEntry.etag {
            request.setValue(etag, forHTTPHeaderField: "If-None-Match")
        }
        if let lastModified = cachedEntry.lastModified {
            request.setValue(lastModified, forHTTPHeaderField: "If-Modified-Since")
        }
    }

    /// Check if a response indicates the cached version is still valid.
    static func isNotModified(_ response: HTTPURLResponse) -> Bool {
        response.statusCode == 304
    }

    /// Extract cache validators from a response.
    static func extractValidators(from response: HTTPURLResponse) -> (etag: String?, lastModified: String?) {
        let etag = response.value(forHTTPHeaderField: "ETag")
        let lastModified = response.value(forHTTPHeaderField: "Last-Modified")
        return (etag, lastModified)
    }
}

HTTPResponseCache.swift

import Foundation

/// Metadata stored alongside cached response data.
struct CachedResponseEntry: Codable, Sendable {
    let data: Data
    let statusCode: Int
    let etag: String?
    let lastModified: String?
    let cacheControl: String?
    let cachedAt: Date
    let maxAge: TimeInterval
    var lastAccessedAt: Date

    /// Whether the entry is fresh (within max-age).
    var isFresh: Bool {
        Date().timeIntervalSince(cachedAt) < maxAge
    }

    /// Whether the entry is within the stale-while-revalidate window.
    func isWithinSWRWindow(swrDuration: TimeInterval) -> Bool {
        let age = Date().timeIntervalSince(cachedAt)
        return age < (maxAge + swrDuration)
    }
}

/// Protocol for HTTP response cache storage.
protocol HTTPResponseCaching: Actor {
    func get(_ key: String) -> CachedResponseEntry?
    func store(_ key: String, entry: CachedResponseEntry)
    func remove(_ key: String)
    func removeAll()
}

/// Disk-backed HTTP response cache with LRU eviction.
actor DiskHTTPResponseCache: HTTPResponseCaching {
    private let cacheDirectory: URL
    private let maxDiskSize: Int
    private var memoryCache: [String: CachedResponseEntry] = [:]
    private let maxMemoryEntries: Int

    init(
        directory: URL? = nil,
        maxDiskSize: Int = 100 * 1024 * 1024,
        maxMemoryEntries: Int = 100
    ) {
        self.cacheDirectory = directory ?? FileManager.default
            .urls(for: .cachesDirectory, in: .userDomainMask)[0]
            .appendingPathComponent("HTTPCache", isDirectory: true)
        self.maxDiskSize = maxDiskSize
        self.maxMemoryEntries = maxMemoryEntries

        try? FileManager.default.createDirectory(
            at: cacheDirectory,
            withIntermediateDirectories: true
        )
    }

    func get(_ key: String) -> CachedResponseEntry? {
        // Check memory first
        if var entry = memoryCache[key] {
            entry.lastAccessedAt = Date()
            memoryCache[key] = entry
            return entry
        }

        // Check disk
        let fileURL = cacheDirectory.appendingPathComponent(key.sha256Hash)
        guard let data = try? Data(contentsOf: fileURL),
              var entry = try? JSONDecoder().decode(CachedResponseEntry.self, from: data) else {
            return nil
        }

        // Promote to memory
        entry.lastAccessedAt = Date()
        memoryCache[key] = entry
        evictMemoryIfNeeded()

        return entry
    }

    func store(_ key: String, entry: CachedResponseEntry) {
        // Store in memory
        memoryCache[key] = entry
        evictMemoryIfNeeded()

        // Store on disk
        let fileURL = cacheDirectory.appendingPathComponent(key.sha256Hash)
        if let data = try? JSONEncoder().encode(entry) {
            try? data.write(to: fileURL)
        }

        Task { await evictDiskIfNeeded() }
    }

    func remove(_ key: String) {
        memoryCache.removeValue(forKey: key)
        let fileURL = cacheDirectory.appendingPathComponent(key.sha256Hash)
        try? FileManager.default.removeItem(at: fileURL)
    }

    func removeAll() {
        memoryCache.removeAll()
        try? FileManager.default.removeItem(at: cacheDirectory)
        try? FileManager.default.createDirectory(
            at: cacheDirectory,
            withIntermediateDirectories: true
        )
    }

    // MARK: - Eviction

    private func evictMemoryIfNeeded() {
        guard memoryCache.count > maxMemoryEntries else { return }
        let sorted = memoryCache.sorted { $0.value.lastAccessedAt < $1.value.lastAccessedAt }
        let toRemove = sorted.prefix(memoryCache.count - maxMemoryEntries)
        for (key, _) in toRemove {
            memoryCache.removeValue(forKey: key)
        }
    }

    private func evictDiskIfNeeded() {
        let fileManager = FileManager.default
        guard let contents = try? fileManager.contentsOfDirectory(
            at: cacheDirectory,
            includingPropertiesForKeys: [.fileSizeKey, .contentAccessDateKey],
            options: .skipsHiddenFiles
        ) else { return }

        let totalSize = contents.reduce(0) { total, url in
            let size = (try? url.resourceValues(forKeys: [.fileSizeKey]))?.fileSize ?? 0
            return total + size
        }

        guard totalSize > maxDiskSize else { return }

        // Sort by access date, remove oldest
        let sorted = contents.sorted { a, b in
            let dateA = (try? a.resourceValues(forKeys: [.contentAccessDateKey]))?.contentAccessDate ?? .distantPast
            let dateB = (try? b.resourceValues(forKeys: [.contentAccessDateKey]))?.contentAccessDate ?? .distantPast
            return dateA < dateB
        }

        var currentSize = totalSize
        for url in sorted {
            guard currentSize > maxDiskSize else { break }
            let size = (try? url.resourceValues(forKeys: [.fileSizeKey]))?.fileSize ?? 0
            try? fileManager.removeItem(at: url)
            currentSize -= size
        }
    }
}

/// In-memory cache for testing.
actor InMemoryHTTPResponseCache: HTTPResponseCaching {
    private var storage: [String: CachedResponseEntry] = [:]

    func get(_ key: String) -> CachedResponseEntry? { storage[key] }
    func store(_ key: String, entry: CachedResponseEntry) { storage[key] = entry }
    func remove(_ key: String) { storage.removeValue(forKey: key) }
    func removeAll() { storage.removeAll() }
}

// MARK: - String Hashing Extension

extension String {
    /// SHA-256 hash for safe file names.
    var sha256Hash: String {
        import CryptoKit
        let data = Data(self.utf8)
        let hash = SHA256.hash(data: data)
        return hash.compactMap { String(format: "%02x", $0) }.joined()
    }
}

CachingAPIClient.swift

import Foundation

/// Decorator that adds HTTP caching to any APIClient conformance.
///
/// Uses the decorator pattern: wraps an existing client without modifying it.
/// Cache behavior is controlled per-endpoint via `CacheConfigurable`.
final class CachingAPIClient: APIClient, Sendable {
    private let wrapped: APIClient
    private let cache: any HTTPResponseCaching
    private let configuration: HTTPCacheConfiguration
    private let reachability: NetworkReachability?

    init(
        wrapping client: APIClient,
        cache: any HTTPResponseCaching,
        configuration: HTTPCacheConfiguration = .default,
        reachability: NetworkReachability? = nil
    ) {
        self.wrapped = client
        self.cache = cache
        self.configuration = configuration
        self.reachability = reachability
    }

    func request<E: APIEndpoint>(_ endpoint: E) async throws -> E.Response {
        let cacheKey = Self.cacheKey(for: endpoint)
        let policy = (endpoint as? CacheConfigurable)?.cachePolicy ?? configuration.defaultPolicy

        switch policy {
        case .noCache:
            return try await wrapped.request(endpoint)

        case .forceCache:
            if let cached = await cache.get(cacheKey) {
                return try JSONDecoder().decode(E.Response.self, from: cached.data)
            }
            return try await fetchAndCache(endpoint, key: cacheKey)

        case .cacheFirst(let maxAge):
            if let cached = await cache.get(cacheKey), cached.isFresh {
                return try JSONDecoder().decode(E.Response.self, from: cached.data)
            }
            // Stale-while-revalidate: return stale and refresh
            if let cached = await cache.get(cacheKey) {
                let decoded = try JSONDecoder().decode(E.Response.self, from: cached.data)
                Task { try? await self.fetchAndCache(endpoint, key: cacheKey) }
                return decoded
            }
            return try await fetchAndCache(endpoint, key: cacheKey, maxAge: maxAge)

        case .default, .custom:
            return try await handleDefault(endpoint, key: cacheKey, policy: policy)
        }
    }

    // MARK: - Private

    private func handleDefault<E: APIEndpoint>(
        _ endpoint: E,
        key: String,
        policy: CachePolicy
    ) async throws -> E.Response {
        // Check cache
        if let cached = await cache.get(key), cached.isFresh {
            return try JSONDecoder().decode(E.Response.self, from: cached.data)
        }

        // Try network
        do {
            let maxAge: TimeInterval? = if case .custom(let age) = policy { age } else { nil }
            return try await fetchAndCache(endpoint, key: key, maxAge: maxAge)
        } catch {
            // Offline fallback
            if configuration.offlineFallbackEnabled,
               let cached = await cache.get(key) {
                return try JSONDecoder().decode(E.Response.self, from: cached.data)
            }
            throw error
        }
    }

    @discardableResult
    private func fetchAndCache<E: APIEndpoint>(
        _ endpoint: E,
        key: String,
        maxAge: TimeInterval? = nil
    ) async throws -> E.Response {
        let response = try await wrapped.request(endpoint)

        let data = try JSONEncoder().encode(response)
        let entry = CachedResponseEntry(
            data: data,
            statusCode: 200,
            etag: nil,
            lastModified: nil,
            cacheControl: nil,
            cachedAt: Date(),
            maxAge: maxAge ?? configuration.defaultMaxAge,
            lastAccessedAt: Date()
        )
        await cache.store(key, entry: entry)

        return response
    }

    private static func cacheKey<E: APIEndpoint>(for endpoint: E) -> String {
        let method = "\(endpoint.method)"
        let path = endpoint.path
        let query = endpoint.queryItems?
            .sorted { $0.name < $1.name }
            .map { "\($0.name)=\($0.value ?? "")" }
            .joined(separator: "&") ?? ""
        return "\(method)|\(path)|\(query)"
    }
}

NetworkReachability.swift

import Foundation
import Network

/// Monitors network connectivity using NWPathMonitor.
///
/// Use to detect offline state and enable stale cache fallback.
actor NetworkReachability {
    private let monitor: NWPathMonitor
    private(set) var isConnected: Bool = true
    private(set) var isExpensive: Bool = false

    init() {
        self.monitor = NWPathMonitor()
    }

    func start() {
        monitor.pathUpdateHandler = { [weak self] path in
            Task { [weak self] in
                await self?.updatePath(path)
            }
        }
        monitor.start(queue: DispatchQueue(label: "com.app.network.monitor"))
    }

    func stop() {
        monitor.cancel()
    }

    private func updatePath(_ path: NWPath) {
        isConnected = path.status == .satisfied
        isExpensive = path.isExpensive
    }
}

Source: SKILL.md on GitHub

1 alert16d5 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The 'generators' skill provides a robust set of templates and workflows for generating Swift code for iOS and macOS applications. Security analysis identified a surface for indirect prompt injection because the skill reads local project files to adapt its generation logic. It also utilizes dynamic execution by generating and running local Swift scripts to create application icons and uses shell commands to process images and audit project code. All external references are to well-known, established services.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: MEDIUM · 1 issue

  • Runlayer7mo

    59/179 files flagged

  • ZeroLeaks5mo

    Scan incomplete

Signed by skilld at 30e898f. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 months ago.

Steadyupdated 2 months ago
What it can do
Reads files Edits files Runs commands
last_verified
2026-07-16
review_by
2027-06-22
os_version
iOS 27 / macOS 27
All 7 allowed tools
ReadWriteEditGlobGrepBashAskUserQuestion

README badge

README badge for rshankras/claude-code-apple-skills/generators