CodableCache

SwiftiOSCache LibraryCodableMemory CacheDisk CacheNSCache

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()
    }
}