Cache (Swift)
ライブラリ
Cache (Swift)
概要
CacheはSwift Package Managerに対応したモダンなSwiftキャッシュライブラリです。メモリとディスクの両方に対応し、時間制限や容量制限をサポートするiOS・macOS向けのキャッシュソリューションです。
詳細
Cache(キャッシュ)は、Swift Package Managerの普及により注目度が上昇している、モダンなSwift開発向けのキャッシュライブラリです。メモリとディスクの両方でのキャッシュをサポートし、TTL(Time To Live)による時間制限と容量制限機能を提供します。iOS 9/macOS 10.11以降で動作し、Codableプロトコルに準拠したカスタム型やプリミティブ型(String、Intなど)のキャッシュが可能です。Swift Package Manager(SPM)エコシステムとの統合により、モダンなSwift開発ワークフローに自然に組み込むことができ、0xLeifによって開発されています。NSCacheやHanekeSwiftといった他のSwiftキャッシュライブラリと比較して、SPMネイティブサポートとモダンなSwift言語機能を活用した設計が特徴です。
メリット・デメリット
メリット
- Swift Package Manager対応: モダンなSwift開発環境との完全統合
- メモリ・ディスク両対応: 柔軟なキャッシュ戦略の実装
- Codable対応: 型安全なキャッシュ操作とSwiftネイティブなシリアライゼーション
- 時間・容量制限: TTLと容量制限による自動管理
- iOS/macOS対応: Apple プラットフォーム全般での使用
- シンプルAPI: 直感的で使いやすいインターフェース
- 軽量設計: 最小限の依存関係とメモリフットプリント
デメリット
- Apple プラットフォーム限定: iOS・macOS以外での使用不可
- 新しいライブラリ: 実績が他の成熟したライブラリより少ない
- 機能制限: 高度なキャッシュ機能は他の専用ライブラリに劣る
- コミュニティサイズ: NSCacheなどの標準ライブラリと比較してコミュニティが小さい
- ドキュメント: 包括的なドキュメントが限定的
主要リンク
- Swift Package Index - Cache
- GitHub リポジトリ
- Swift Package Manager 公式
- Codable プロトコル ドキュメント
- NSCache ドキュメント
- Swift by Sundell - Caching記事
書き方の例
Swift Package Managerでのインストール
// Package.swift
import PackageDescription
let package = Package(
name: "MyProject",
dependencies: [
.package(url: "https://github.com/0xLeif/Cache", from: "1.0.0")
],
targets: [
.target(
name: "MyProject",
dependencies: ["Cache"]
)
]
)
基本的なキャッシュ使用
import Cache
// Cacheインスタンスを作成
let cache = Cache<String, String>()
// 値を設定
cache.set("user:123", value: "John Doe")
// 値を取得
if let userName = cache.get("user:123") {
print("User name: \(userName)")
}
// 値を削除
cache.remove("user:123")
// キャッシュクリア
cache.clear()
TTL(Time To Live)付きキャッシュ
import Cache
// TTL対応キャッシュ
let cache = Cache<String, String>(defaultTTL: 300) // 5分
// TTLを指定して値を設定
cache.set("temporary_data", value: "This will expire", ttl: 60) // 1分で期限切れ
// 期限切れチェック
if cache.isExpired("temporary_data") {
print("Data has expired")
} else {
let data = cache.get("temporary_data")
print("Data: \(data ?? "nil")")
}
Codable型のキャッシュ
import Cache
// Codable準拠の構造体
struct User: Codable {
let id: String
let name: String
let email: String
let createdAt: Date
}
// Userオブジェクト用キャッシュ
let userCache = Cache<String, User>()
// ユーザーオブジェクトをキャッシュ
let user = User(
id: "123",
name: "Jane Smith",
email: "[email protected]",
createdAt: Date()
)
userCache.set("user:\(user.id)", value: user, ttl: 3600) // 1時間
// ユーザーオブジェクトを取得
if let cachedUser = userCache.get("user:123") {
print("Cached user: \(cachedUser.name)")
}
キャッシュサイズ制限
import Cache
// 最大容量制限付きキャッシュ
let cache = Cache<String, Data>(maxSize: 100) // 最大100アイテム
// 容量超過時の動作確認
for i in 1...150 {
let key = "item:\(i)"
let data = "Data for item \(i)".data(using: .utf8)!
cache.set(key, value: data)
}
print("Cache size: \(cache.count)") // 100 (最大容量)
print("First item exists: \(cache.get("item:1") != nil)") // false (削除済み)
print("Last item exists: \(cache.get("item:150") != nil)") // true
非同期キャッシュ操作
import Cache
class AsyncCacheService {
private let cache = Cache<String, Data>()
private let queue = DispatchQueue(label: "cache.queue", attributes: .concurrent)
func setData(_ data: Data, forKey key: String, completion: @escaping () -> Void) {
queue.async(flags: .barrier) {
self.cache.set(key, value: data)
DispatchQueue.main.async {
completion()
}
}
}
func getData(forKey key: String, completion: @escaping (Data?) -> Void) {
queue.async {
let data = self.cache.get(key)
DispatchQueue.main.async {
completion(data)
}
}
}
func clearCache(completion: @escaping () -> Void) {
queue.async(flags: .barrier) {
self.cache.clear()
DispatchQueue.main.async {
completion()
}
}
}
}
// 使用例
let cacheService = AsyncCacheService()
cacheService.setData("Hello, Cache!".data(using: .utf8)!, forKey: "greeting") {
print("Data cached successfully")
}
cacheService.getData(forKey: "greeting") { data in
if let data = data, let string = String(data: data, encoding: .utf8) {
print("Retrieved: \(string)")
}
}
メモリ・ディスク複合キャッシュ
import Cache
import Foundation
class HybridCache<Key: Hashable & Codable, Value: Codable> {
private let memoryCache = Cache<Key, Value>(maxSize: 50) // メモリ最大50アイテム
private let diskCacheURL: URL
init(diskCacheDirectory: String = "HybridCache") {
let documentsPath = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first!
self.diskCacheURL = documentsPath.appendingPathComponent(diskCacheDirectory)
// ディレクトリ作成
try? FileManager.default.createDirectory(at: diskCacheURL,
withIntermediateDirectories: true)
}
func set(_ key: Key, value: Value, ttl: TimeInterval? = nil) {
// メモリキャッシュに保存
memoryCache.set(key, value: value, ttl: ttl)
// ディスクキャッシュに保存
saveToDisk(key: key, value: value)
}
func get(_ key: Key) -> Value? {
// まずメモリから試行
if let value = memoryCache.get(key) {
return value
}
// ディスクから取得してメモリに復元
if let value = loadFromDisk(key: key) {
memoryCache.set(key, value: value)
return value
}
return nil
}
private func saveToDisk(key: Key, value: Value) {
do {
let data = try JSONEncoder().encode(value)
let url = diskCacheURL.appendingPathComponent("\(key).cache")
try data.write(to: url)
} catch {
print("Failed to save to disk: \(error)")
}
}
private func loadFromDisk(key: Key) -> Value? {
do {
let url = diskCacheURL.appendingPathComponent("\(key).cache")
let data = try Data(contentsOf: url)
return try JSONDecoder().decode(Value.self, from: data)
} catch {
return nil
}
}
}
// 使用例
let hybridCache = HybridCache<String, User>()
hybridCache.set("user:456", value: User(
id: "456",
name: "Bob Wilson",
email: "[email protected]",
createdAt: Date()
))
if let user = hybridCache.get("user:456") {
print("Retrieved user: \(user.name)")
}
キャッシュ統計とモニタリング
import Cache
class MonitoredCache<Key: Hashable, Value> {
private let cache = Cache<Key, Value>()
private var hitCount = 0
private var missCount = 0
private var setCount = 0
var hitRate: Double {
let total = hitCount + missCount
return total > 0 ? Double(hitCount) / Double(total) : 0
}
func set(_ key: Key, value: Value, ttl: TimeInterval? = nil) {
cache.set(key, value: value, ttl: ttl)
setCount += 1
}
func get(_ key: Key) -> Value? {
if let value = cache.get(key) {
hitCount += 1
return value
} else {
missCount += 1
return nil
}
}
func statistics() -> (hits: Int, misses: Int, sets: Int, hitRate: Double) {
return (hitCount, missCount, setCount, hitRate)
}
func resetStatistics() {
hitCount = 0
missCount = 0
setCount = 0
}
}
// 使用例
let monitoredCache = MonitoredCache<String, String>()
monitoredCache.set("key1", value: "value1")
monitoredCache.set("key2", value: "value2")
_ = monitoredCache.get("key1") // ヒット
_ = monitoredCache.get("key3") // ミス
let stats = monitoredCache.statistics()
print("Hit rate: \(stats.hitRate)") // 0.5 (50%)