Caching in Swift (iOS/macOS): Redis, NSCache, and Core Data

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Caching in Swift (iOS/macOS): from in-memory to Redis



In iOS and macOS development, caching is a key tool for speeding up applications, reducing server load, and improving user experience. In this article, we will look at the main approaches to caching in Swift: from simple NSCache to professional solutions based on Redis and SQLite. Each method has its own area of application, and the right choice depends on the data type, its volume, and persistence requirements.



1. NSCache — built-in in-memory caching



NSCache is a lightweight, thread-safe cache built into Foundation. It automatically frees memory when resources are low, making it ideal for caching images, API data, and other temporary objects.



import Foundation

class ImageCache { private let cache = NSCache<NSString, UIImage>() func getImage(forKey key: String) -> UIImage? { return cache.object(forKey: key as NSString) } func setImage(_ image: UIImage, forKey key: String) { cache.setObject(image, forKey: key as NSString) } func removeImage(forKey key: String) { cache.removeObject(forKey: key as NSString) }}


Advantages:

  • No setup required — works out of the box.
  • Automatically manages memory (removes objects on didReceiveMemoryWarning).
  • Thread-safe.


Disadvantages:

  • Data is lost when the application restarts.
  • Not suitable for large volumes of data (limited by RAM).


2. Core Data as a persistent cache



Core Data is often used not only as an ORM but also as a cache for structured data. This is convenient when you need to save complex objects with relationships and perform filtering.



import CoreData

class CoreDataCache { private let context: NSManagedObjectContext init(context: NSManagedObjectContext) { self.context = context } func cacheResponse(_ data: Data, forKey key: String) { let cached = CachedResponse(context: context) cached.key = key cached.data = data cached.timestamp = Date() try? context.save() } func getCachedResponse(forKey key: String) -> Data? { let fetchRequest: NSFetchRequest<CachedResponse> = CachedResponse.fetchRequest() fetchRequest.predicate = NSPredicate(format: "key == %@", key) fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: false)] fetchRequest.fetchLimit = 1 guard let result = try? context.fetch(fetchRequest).first else { return nil } return result.data }}


When to use:

  • Data must persist between sessions.
  • Complex filtering is needed (e.g., text search).
  • Data volume is moderate (up to several hundred thousand records).


3. Redis — server-side caching for Swift



Redis is a high-performance in-memory data store that works over the network. In the context of Swift (iOS/macOS), Redis is most often used on the server side (Vapor, Kitura) for caching sessions, results of complex queries, and task queues. However, there are client libraries that allow connecting to Redis directly from a mobile application (e.g., via RediStack).



import RediStack

final class RedisCache { private let connection: RedisConnection init(host: String, port: Int = 6379) async throws { self.connection = try await RedisConnection.connect( to: .init(hostname: host, port: port) ) }

Recommendations