Кэширование в Swift: Redis, Memcached и встроенные решения от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Кэширование в Swift: Redis, Memcached и встроенные решения: от А до Я

Кэширование — одна из ключевых стратегий оптимизации производительности в iOS и macOS приложениях. В этой статье мы подробно разберем, как организовать кэширование в Swift: от встроенного NSCache до работы с внешними системами вроде Redis и Memcached. Вы узнаете, когда какой инструмент выбрать, и получите готовые примеры кода для каждого подхода.

Зачем нужно кэширование в Swift-приложениях?

Любое приложение, которое работает с сетевыми запросами, базами данных или тяжелыми вычислениями, может выиграть от кэширования. Основные преимущества:

  • Скорость: данные из кэша возвращаются за миллисекунды, без задержек на сеть или диск.
  • Экономия трафика: меньше запросов к серверу — меньше расходов для пользователя и сервера.
  • Офлайн-доступ: при правильной реализации часть функционала работает без интернета.
  • Снижение нагрузки на сервер: особенно актуально для серверных Swift-решений (Vapor, Kitura).

Встроенные решения: NSCache и URLCache

NSCache — легковесное кэширование в памяти

NSCache — это встроенный в Foundation класс для временного хранения пар "ключ-значение". Он автоматически удаляет объекты при нехватке памяти, что делает его идеальным для кэширования изображений, JSON-ответов и других некритичных данных.

import Foundation

class ImageCacheManager {
    static let shared = ImageCacheManager()
    private let cache = NSCache<NSString, NSData>()
    
    func setImageData(_ data: Data, forKey key: String) {
        cache.setObject(data as NSData, forKey: key as NSString)
    }
    
    func getImageData(forKey key: String) -> Data? {
        return cache.object(forKey: key as NSString) as Data?
    }
    
    func removeAll() {
        cache.removeAllObjects()
    }
}

// Использование
let cache = ImageCacheManager.shared
cache.setImageData(imageData, forKey: "avatar_123")
if let cachedData = cache.getImageData(forKey: "avatar_123") {
    print("Данные из кэша: \(cachedData.count) байт")
}

Особенности:

  • Автоматическое управление памятью — не нужно вручную удалять объекты.
  • Потокобезопасен — можно использовать из разных потоков.
  • Не гарантирует сохранность данных — кэш может быть очищен системой.

URLCache — кэширование HTTP-запросов

URLCache обеспечивает кэширование на уровне сетевых запросов. Он работает с URLSession и автоматически сохраняет ответы сервера.

import Foundation

// Настройка кэша
let memoryCapacity = 50 * 1024 * 1024 // 50 МБ
let diskCapacity = 100 * 1024 * 1024 // 100 МБ
let urlCache = URLCache(memoryCapacity: memoryCapacity, 
                        diskCapacity: diskCapacity, 
                        diskPath: "myCache")

URLCache.shared = urlCache

// Конфигурация сессии
let config = URLSessionConfiguration.default
config.requestCachePolicy = .returnCacheDataElseLoad
config.urlCache = urlCache

let session = URLSession(configuration: config)

// Выполнение запроса
let url = URL(string: "https://api.example.com/data")!
let task = session.dataTask(with: url) { data, response, error in
    if let data = data {
        print("Получено \(data.count) байт")
        // Данные автоматически кэшируются
    }
}
task.resume()

Для тонкой настройки кэширования используйте заголовки HTTP и политики Cache-Control на стороне сервера.

Redis в Swift: серверное кэширование

Redis — это высокопроизводительное хранилище "ключ-значение" в памяти. Для Swift-разработчиков, работающих с серверными фреймворками (Vapor, Perfect), Redis — стандартный выбор для кэширования.

Подключение Redis к Vapor

// Package.swift
// dependencies: [
//     .package(url: "https://github.com/vapor/redis.git", from: "4.0.0")
// ]

import Vapor
import Redis

// Конфигурация в configure.swift
app.redis.configuration = try RedisConfiguration(
    hostname: "localhost",
    port: 6379,
    password: nil
)

// Кэширование данных
func cacheUser(_ user: User, on req: Request) async throws {
    let key = "user:\(user.id)"
    let data = try JSONEncoder().encode(user)
    try await req.redis.set(key, to: data)
    try await req.redis.expire(key, after: .seconds(3600)) // TTL 1 час
}

// Получение из кэша
func getUserFromCache(id: Int, on req: Request) async throws -> User? {
    let key = "user:\(id)"
    guard let data = try await req.redis.get(key, as: Data.self) else {
        return nil
    }
    return try JSONDecoder().decode(User.self, from: data)
}

Преимущества Redis:

  • Поддержка сложных структур данных (списки, множества, хэши).
  • TTL (Time To Live) для автоматического удаления устаревших данных.
  • Поддержка Pub/Sub для уведомлений.
  • Кластеризация и репликация для высокой доступности.

Memcached в Swift: простое и быстрое кэширование

Memcached — еще одна популярная система кэширования, ориентированная на простоту и скорость. В отличие от Redis, Memcached поддерживает только базовые операции с ключами и значениями.

Работа с Memcached через Swift

Для работы с Memcached в Swift можно использовать библиотеку swift-memcached или написать обертку над libmemcached.

import Foundation
import libmemcached

class MemcachedClient {
    private var memc: memcached_st?
    
    init(server: String, port: UInt32 = 11211) {
        memc = memcached_create(nil)
        var rc = memcached_server_add(memc, server, port)
        // Обработка ошибок опущена для краткости
    }
    
    func set(key: String, value: String, ttl: Int = 300) -> Bool {
        let keyData = key.data(using: .utf8)!
        let valueData = value.data(using: .utf8)!
        
        var rc = memcached_set(memc, 
                               keyData.bytes, keyData.count,
                               value                

Блоги

Рекомендаций по книгам