Кэширование в Swift (iOS/macOS): от in-memory до Redis
В разработке под iOS и macOS кэширование — ключевой инструмент для ускорения работы приложений, снижения нагрузки на сервер и улучшения пользовательского опыта. В этой статье мы рассмотрим основные подходы к кэшированию в Swift: от простого NSCache до профессиональных решений на базе Redis и SQLite. Каждый метод имеет свою область применения, и правильный выбор зависит от типа данных, их объёма и требований к персистентности.
1. NSCache — встроенное in-memory кэширование
NSCache — это лёгкий, потокобезопасный кэш, встроенный в Foundation. Он автоматически освобождает память при нехватке ресурсов, что делает его идеальным для кэширования изображений, данных API и других временных объектов.
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)
}
}
Преимущества:
- Не требует настройки — работает «из коробки».
- Автоматически управляет памятью (удаляет объекты при
didReceiveMemoryWarning). - Потокобезопасен.
Недостатки:
- Данные теряются при перезапуске приложения.
- Не подходит для больших объёмов данных (ограничен RAM).
2. Core Data как персистентный кэш
Core Data часто используют не только как ORM, но и как кэш для структурированных данных. Это удобно, когда нужно сохранить сложные объекты с отношениями и выполнять фильтрацию.
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
}
}
Когда использовать:
- Данные должны сохраняться между сессиями.
- Нужна сложная фильтрация (например, поиск по тексту).
- Объём данных умеренный (до нескольких сотен тысяч записей).
3. Redis — серверное кэширование для Swift
Redis — это высокопроизводительное in-memory хранилище, работающее по сети. В контексте Swift (iOS/macOS) Redis чаще всего используется на серверной стороне (Vapor, Kitura) для кэширования сессий, результатов сложных запросов и очередей задач. Однако существуют клиентские библиотеки, позволяющие подключаться к Redis напрямую из мобильного приложения (например, через 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)
)
}
func set(key: String, value: String, ttl: Int = 3600) async throws {
try await connection.set(RedisKey(key), to: value)
try await connection.expire(RedisKey(key), after: .seconds(ttl))
}
func get(key: String) async throws -> String? {
return try await connection.get(RedisKey(key))?.string
}
func delete(key: String) async throws {
try await connection.delete(RedisKey(key))
}
}
Преимущества Redis:
- Миллисекундная задержка.
- Поддержка TTL (автоматическое удаление устаревших данных).
- Может обслуживать множество клиентов одновременно.
Когда использовать Redis на iOS/macOS:
- Синхронизация данных между устройствами (через общий сервер).
- Кэширование результатов серверных запросов для уменьшения нагрузки на API.
- Очереди задач и Pub/Sub для real-time