CodableCache
Cache Library
CodableCache
Overview
CodableCache is a memory and disk cache library for iOS using Swift 4's Codable protocol.
Details
CodableCache is a simple and reliable cache library based on NSCache, NSKeyedArchive, and Swift 4's Codable protocol. Developed by Adam Sowers, this library enables seamless memory caching and disk persistence of plain Swift structs. Designed as a replacement for NSCoding-based frameworks, you simply need to define a model and conform to Codable to get started. It uses NSCache for memory storage, providing automatic eviction when the system runs low on memory. For disk storage, you can specify directories like .cachesDirectory or .applicationSupportDirectory, making it suitable for persisting application settings and user data. Unlike heavyweight solutions like CoreData, Realm, or SQLite, it's optimized for backing up local state based on JSON APIs, enabling rapid model definition, reduced boilerplate code, and lightning-fast data saving. The framework allows for seamless memory caching and disk persistence of your plain old Swift structs with minimal setup.
Pros and Cons
Pros
- Codable Integration: Complete integration with Swift 4's Codable protocol
- Dual Storage: Both memory and disk caching capabilities
- NSCache Utilization: Automatic eviction through system memory management
- Easy Installation: Support for CocoaPods, Carthage, and Swift Package Manager
- Type Safety: Safe cache operations through Swift's type system
- Custom Directories: Configurable persistence directory options
- Lightweight Design: Simple implementation focused on essential features
Cons
- iOS Only: Limited to Swift and iOS environments
- Limited Features: Not suitable for complex queries or relationship management
- Cache Strategy: Advanced cache algorithms like LRU not implemented
- Concurrency: Limited synchronization features for multi-threaded environments
- Size Control: Difficult to control detailed cache size management
Key Links
Usage Examples
Basic Setup
import CodableCache
struct Person: Codable {
let name: String
let age: Double // Can express half ages for kids
}
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 Data Caching
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("Failed to cache response: \(error)")
}
}
func getCachedResponse() -> APIResponse? {
return cache.get()
}
func clearCache() {
cache.removeValue()
}
}
App Settings Persistence
import CodableCache
struct AppSettings: Codable {
var theme: String = "light"
var notifications: Bool = true
var language: String = "en"
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("Failed to save settings: \(error)")
}
}
func updateTheme(_ theme: String) {
var settings = loadSettings()
settings.theme = theme
saveSettings(settings)
}
}
// Usage example
let manager = SettingsManager()
var settings = manager.loadSettings()
settings.theme = "dark"
manager.saveSettings(settings)
Custom Directory Persistence
import CodableCache
struct UserProfile: Codable {
let userId: String
let displayName: String
let avatarURL: String?
let lastLoginDate: Date
}
class UserProfileCache {
// Persistent storage not purged by system
private let persistentCache = CodableCache<UserProfile>(
key: "user_profile",
directory: .applicationSupportDirectory
)
// Temporary cache (can be purged by system)
private let temporaryCache = CodableCache<[String]>(
key: "recent_searches",
directory: .cachesDirectory
)
func saveUserProfile(_ profile: UserProfile) {
do {
try persistentCache.set(value: profile)
print("User profile persisted successfully")
} catch {
print("Profile save error: \(error)")
}
}
func loadUserProfile() -> UserProfile? {
return persistentCache.get()
}
func saveRecentSearches(_ searches: [String]) {
do {
try temporaryCache.set(value: searches)
} catch {
print("Search history save error: \(error)")
}
}
}
Multiple Cache Management
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")
// User information management
func cacheCurrentUser(_ user: User) {
try? userCache.set(value: user)
}
func getCurrentUser() -> User? {
return userCache.get()
}
// Posts data management
func cachePosts(_ posts: [Post]) {
try? postsCache.set(value: posts)
}
func getCachedPosts() -> [Post] {
return postsCache.get() ?? []
}
// Configuration data management
func cacheConfig(_ config: AppConfig) {
try? configCache.set(value: config)
}
func getConfig() -> AppConfig? {
return configCache.get()
}
// Clear all caches
func clearAllCaches() {
userCache.removeValue()
postsCache.removeValue()
configCache.removeValue()
}
}
Network Response Cache Practice
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("Cached \(articles.count) articles")
} catch {
print("Article cache error: \(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()
}
}