CodableCache
キャッシュライブラリ
CodableCache
概要
CodableCacheは、Swift 4のCodableプロトコルを使用したiOS向けのメモリ・ディスクキャッシュライブラリです。
詳細
CodableCache(コーダブルキャッシュ)は、NSCache、NSKeyedArchive、そしてSwift 4のCodableプロトコルをベースにした、シンプルで信頼性の高いキャッシュライブラリです。Adam Sowersによって開発されたこのライブラリは、プレーンなSwift構造体のシームレスなメモリキャッシュとディスク永続化を可能にします。従来のNSCodingベースのフレームワークの代替として設計されており、モデルを定義してCodableに準拠させるだけで使用を開始できます。メモリストレージにはNSCacheを使用し、システムがメモリ不足になった際の自動削除機能を提供します。ディスクストレージには.cachesDirectoryや.applicationSupportDirectoryなどを指定でき、アプリケーション設定やユーザーデータの永続化に適しています。CoreData、Realm、SQLiteのような重厚なソリューションとは異なり、JSON APIに基づくローカル状態のバックアップに最適化されており、迅速なモデル定義、ボイラープレートコードの削減、高速なデータ保存を実現します。
メリット・デメリット
メリット
- Codable対応: Swift 4のCodableプロトコルとの完全統合
- 二重ストレージ: メモリとディスクの両方でキャッシュ
- NSCache活用: システムメモリ管理による自動削除
- 簡単導入: CocoaPods、Carthage、Swift Package Managerに対応
- 型安全性: Swiftの型システムによる安全なキャッシュ操作
- カスタムディレクトリ: 永続化先ディレクトリの指定可能
- 軽量設計: 必要最小限の機能に絞ったシンプルな実装
デメリット
- iOS限定: SwiftとiOS環境でのみ利用可能
- 機能限定: 複雑なクエリや関係性管理には不向き
- キャッシュ戦略: LRUなどの高度なキャッシュアルゴリズムは未実装
- 並行性: マルチスレッド環境での同期機能が限定的
- サイズ制限: キャッシュサイズの詳細制御が困難
主要リンク
書き方の例
基本的なセットアップ
import CodableCache
struct Person: Codable {
let name: String
let age: Double // 子供の半年齢も表現可能
}
final class PersonManager {
let cache: CodableCache<Person>
init(cacheKey: AnyHashable) {
cache = CodableCache<Person>(key: cacheKey)
}
func savePerson(_ person: Person) {
do {
try cache.set(value: person)
print("Person saved successfully")
} catch {
print("Failed to save person: \(error)")
}
}
func loadPerson() -> Person? {
return cache.get()
}
}
JSONデータのキャッシュ
import CodableCache
import Foundation
struct APIResponse: Codable {
let users: [User]
let totalCount: Int
}
struct User: Codable {
let id: Int
let name: String
let email: String
}
class APICache {
private let cache = CodableCache<APIResponse>(key: "api_response")
func cacheAPIResponse(_ response: APIResponse) {
do {
try cache.set(value: response)
} catch {
print("キャッシュの保存に失敗: \(error)")
}
}
func getCachedResponse() -> APIResponse? {
return cache.get()
}
func clearCache() {
cache.removeValue()
}
}
アプリ設定の永続化
import CodableCache
struct AppSettings: Codable {
var theme: String = "light"
var notifications: Bool = true
var language: String = "ja"
var cacheSize: Int = 100
}
class SettingsManager {
private let settingsCache = CodableCache<AppSettings>(
key: "app_settings"
)
func loadSettings() -> AppSettings {
return settingsCache.get() ?? AppSettings()
}
func saveSettings(_ settings: AppSettings) {
do {
try settingsCache.set(value: settings)
} catch {
print("設定の保存に失敗: \(error)")
}
}
func updateTheme(_ theme: String) {
var settings = loadSettings()
settings.theme = theme
saveSettings(settings)
}
}
// 使用例
let manager = SettingsManager()
var settings = manager.loadSettings()
settings.theme = "dark"
manager.saveSettings(settings)
カスタムディレクトリでの永続化
import CodableCache
struct UserProfile: Codable {
let userId: String
let displayName: String
let avatarURL: String?
let lastLoginDate: Date
}
class UserProfileCache {
// システムによって削除されない永続ストレージ
private let persistentCache = CodableCache<UserProfile>(
key: "user_profile",
directory: .applicationSupportDirectory
)
// 一時的なキャッシュ(システムが削除可能)
private let temporaryCache = CodableCache<[String]>(
key: "recent_searches",
directory: .cachesDirectory
)
func saveUserProfile(_ profile: UserProfile) {
do {
try persistentCache.set(value: profile)
print("ユーザープロフィールを永続化しました")
} catch {
print("プロフィール保存エラー: \(error)")
}
}
func loadUserProfile() -> UserProfile? {
return persistentCache.get()
}
func saveRecentSearches(_ searches: [String]) {
do {
try temporaryCache.set(value: searches)
} catch {
print("検索履歴保存エラー: \(error)")
}
}
}
複数のキャッシュ管理
import CodableCache
class DataManager {
private let userCache = CodableCache<User>(key: "current_user")
private let postsCache = CodableCache<[Post]>(key: "user_posts")
private let configCache = CodableCache<AppConfig>(key: "app_config")
// ユーザー情報の管理
func cacheCurrentUser(_ user: User) {
try? userCache.set(value: user)
}
func getCurrentUser() -> User? {
return userCache.get()
}
// 投稿データの管理
func cachePosts(_ posts: [Post]) {
try? postsCache.set(value: posts)
}
func getCachedPosts() -> [Post] {
return postsCache.get() ?? []
}
// 設定データの管理
func cacheConfig(_ config: AppConfig) {
try? configCache.set(value: config)
}
func getConfig() -> AppConfig? {
return configCache.get()
}
// 全キャッシュのクリア
func clearAllCaches() {
userCache.removeValue()
postsCache.removeValue()
configCache.removeValue()
}
}
ネットワークレスポンスキャッシュの実践例
import CodableCache
import Foundation
struct NewsArticle: Codable {
let id: String
let title: String
let content: String
let publishedAt: Date
let author: String
}
class NewsCache {
private let articlesCache = CodableCache<[NewsArticle]>(
key: "latest_articles"
)
private let lastUpdateCache = CodableCache<Date>(
key: "articles_last_update"
)
func cacheArticles(_ articles: [NewsArticle]) {
do {
try articlesCache.set(value: articles)
try lastUpdateCache.set(value: Date())
print("\(articles.count)件の記事をキャッシュしました")
} catch {
print("記事キャッシュエラー: \(error)")
}
}
func getCachedArticles() -> [NewsArticle] {
return articlesCache.get() ?? []
}
func isCacheExpired(maxAge: TimeInterval = 3600) -> Bool {
guard let lastUpdate = lastUpdateCache.get() else {
return true
}
return Date().timeIntervalSince(lastUpdate) > maxAge
}
func getCachedArticlesIfValid() -> [NewsArticle]? {
guard !isCacheExpired() else {
return nil
}
return articlesCache.get()
}
}