Examples

Read the complete executable examples.

Each example is a standalone Swift package. Run it from the repository after moving into its directory or passing its path to SwiftPM.

Memory cache

Use a typed enum key and an identifiable Sendable value. The identifiable overload uses the value's ID as its cache key.

MemoryCacheExample
import SwiftStash

private struct Article: Identifiable, Sendable {
    let id: ArticleKey
    let title: String
}

private enum ArticleKey: String, CacheKey {
    case concurrency
    case actors
}

@main private enum MemoryCacheExample {
    static func main() async {
        let storage = MemoryStorageEngine<ArticleKey, Article>()
        let cache = await Cache(
            policy: .lru(threshold: 10 * 60),
            storagePolicy: storage
        )

        await cache.add(Article(id: .concurrency, title: "A tour of Swift concurrency"))
        await cache.add(Article(id: .actors, title: "Protecting state with actors"))

        if let article = await cache[.concurrency] {
            print("Read: \(article.title)")
        }

        let remainingCount = await cache.evictUntil(maxNbItems: 1)
        print("Entries after eviction: \(remainingCount)")
    }
}

Disk cache with JSON

Make the named directory first. The JSON serializer stores a Codable profile, and the cache reads the profile through its string ID.

DiskCacheExample
import Foundation
import SwiftStash

private struct Profile: Codable, Identifiable, Sendable {
    let id: String
    let displayName: String
}

@main private enum DiskCacheExample {
    static func main() async throws {
        let directoryName = "profile-example"
        try createCacheDirectory(named: directoryName)

        let storage = DiskStorageEngine(
            directory: directoryName,
            serializer: JsonDiskStorageSerializer<Profile>()
        )
        let cache = await Cache(
            policy: .lru(threshold: 24 * 60 * 60),
            storagePolicy: storage
        )

        let profile = Profile(id: "42", displayName: "Ada")
        await cache.add(profile)

        if let cachedProfile = await cache[profile.id] {
            print("Loaded from disk-backed cache: \(cachedProfile.displayName)")
        }
    }

    private static func createCacheDirectory(named name: String) throws {
        let bundleIdentifier = Bundle.main.bundleIdentifier ?? "app-cache"
        let rootDirectory = URL.cachesDirectory
            .appendingPathComponent("\(bundleIdentifier)-cache")
        let directory = rootDirectory.appendingPathComponent(name)

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

Custom disk serializer

A serializer converts one stored type to Data and back. This example persists a temperature as an eight-byte value.

CustomSerializerExample
import Foundation
import SwiftStash

private struct Temperature: Equatable, Sendable {
    let celsius: Double
}

private struct TemperatureSerializer: DiskStorageSerializer {
    func serialize(_ value: Temperature) throws -> Data {
        withUnsafeBytes(of: value.celsius.bitPattern.bigEndian) { Data($0) }
    }

    func deserialize(_ data: Data) throws -> Temperature? {
        guard data.count == MemoryLayout<UInt64>.size else {
            return nil
        }

        var bits: UInt64 = 0
        _ = withUnsafeMutableBytes(of: &bits) { data.copyBytes(to: $0) }
        return Temperature(celsius: Double(bitPattern: UInt64(bigEndian: bits)))
    }
}

@main private enum CustomSerializerExample {
    static func main() async throws {
        let directoryName = "temperature-example"
        try createCacheDirectory(named: directoryName)

        let storage = DiskStorageEngine(
            directory: directoryName,
            serializer: TemperatureSerializer()
        )
        let cache = await Cache(storagePolicy: storage)

        await cache.add(Temperature(celsius: 21.5), for: "office")

        if let temperature = await cache["office"] {
            print("Office: \(temperature.celsius) °C")
        }
    }

    private static func createCacheDirectory(named name: String) throws {
        let bundleIdentifier = Bundle.main.bundleIdentifier ?? "app-cache"
        let rootDirectory = URL.cachesDirectory
            .appendingPathComponent("\(bundleIdentifier)-cache")
        let directory = rootDirectory.appendingPathComponent(name)

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