Реализация миграции кэша при обновлении мобильного приложения
Представьте: вы выпустили обновление, а пользователи массово жалуются на вылеты и старые данные. Причина — неинвалидированный кэш. Старые изображения загружены под старыми ключами, JSON-ответы сериализованы в устаревший формат, HTTP-кэш содержит заголовки с битыми URL. Если не очистить кэш при обновлении — пользователь видит перемешанные данные из разных версий, или приложение крашится на десериализации. Рассмотрим на примере реального проекта: приложение для доставки, где после обновления пользователи видели цены из старого ответа API, что приводило к убыткам. Правильная инвалидация кэша решила проблему за один день. Миграция кэша — обязательный этап любого обновления. Мы, разработчики с 10-летним опытом, собрали проверенные методы для iOS и Android.
Почему кэш становится невалидным после обновления?
Причина — несовместимость форматов данных между версиями. Допустим, в версии 1.0 API возвращал объект { "name": "foo" }, а в 2.0 — { "full_name": "foo bar" }. Если приложение закэшировало старый JSON и пытается десериализовать его в новую модель — краш. Аналогично с изображениями: изменились пути, размеры или хеши. По данным Apple URLCache documentation, HTTP-кэш также может содержать устаревшие заголовки, что приводит к ошибкам загрузки.
Версионирование ключей кэша
Самый простой и надёжный подход: привязать ключи кэша к версии приложения или к версии API.
// Android — пространство имён кэша по версии object CacheKeyBuilder { private val appVersion = BuildConfig.VERSION_CODE fun forImage(imageId: String) = "img_v${appVersion}_$imageId" fun forApiResponse(endpoint: String) = "api_v${appVersion}_$endpoint" } // iOS — ключ с номером билда struct CacheKeyBuilder { static let appVersion = Bundle.main.buildVersionNumber static func imageKey(_ id: String) -> String { "img_v\(appVersion)_\(id)" } static func apiKey(_ endpoint: String) -> String { "api_v\(appVersion)_\(endpoint)" } } При обновлении VERSION_CODE или buildVersionNumber все ключи меняются — старый кэш перестаёт использоваться. Но при этом старые файлы остаются на диске и нужна явная очистка.
Очистка устаревшего кэша при старте
// iOS class CacheManager { private let defaults = UserDefaults.standard private let lastVersionKey = "lastCachedVersion" func cleanupIfNeeded() { let current = Bundle.main.buildVersionNumber let last = defaults.string(forKey: lastVersionKey) ?? "" guard current != last else { return } clearDiskCache() clearURLCache() defaults.set(current, forKey: lastVersionKey) } private func clearDiskCache() { let cacheDir = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first! try? FileManager.default.removeItem(at: cacheDir.appendingPathComponent("ImageCache")) } private func clearURLCache() { URLCache.shared.removeAllCachedResponses() } } Вызываем в application(_:didFinishLaunchingWithOptions:) до инициализации UI.
// Android — очистка при старте class CacheCleaner(private val context: Context) { fun cleanIfNeeded() { val prefs = context.getSharedPreferences("cache", Context.MODE_PRIVATE) val lastVersion = prefs.getInt("lastVersion", 0) val currentVersion = BuildConfig.VERSION_CODE if (lastVersion == currentVersion) return Glide.get(context).clearDiskCache() val cacheDir = File(context.cacheDir, "http-cache") cacheDir.deleteRecursively() prefs.edit().putInt("lastVersion", currentVersion).apply() } } Вызов из Application.onCreate() в фоновом потоке.
Kingfisher (iOS) и Glide (Android)
Обе библиотеки используют собственные дисковые кэши. Kingfisher хранит кэш в Library/Caches/com.onevcat.Kingfisher.ImageCache. Очистка:
KingfisherManager.shared.cache.clearDiskCache() KingfisherManager.shared.cache.clearMemoryCache() Glide: Glide.get(context).clearDiskCache() — только с фонового потока. Официальные репозитории: Kingfisher, Glide.
Сравнение подходов
| Подход | Сложность | Время выполнения | Риск потери данных |
|---|---|---|---|
| Полная очистка кэша | Низкая | 0.5 дня | Высокий (все временные данные пропадают) |
| Версионирование ключей + фоновая очистка старых | Средняя | 1 день | Низкий (важный кэш сохраняется до устаревания) |
| Инкрементальная миграция | Высокая | 2-3 дня | Очень низкий (только несовместимые записи) |
Как выбрать стратегию миграции кэша?
Для небольших проектов достаточно полной очистки при каждом обновлении — это быстро реализовать и легко проверить. Если важна скорость запуска и пользовательский опыт, выбирайте версионирование ключей с фоновой очисткой. Для финансовых или медицинских приложений, где каждая запись критична, используйте инкрементальную миграцию с точечной инвалидацией.
Что входит в работу
| Этап | Результат |
|---|---|
| Аудит кэширования | Описание всех точек кэширования, текущие риски |
| Проектирование стратегии | Выбор подхода под вашу архитектуру |
| Реализация модулей | Native-код для iOS и Android с Unit-тестами |
| Интеграция и тестирование | Проверка на нескольких версиях приложения |
| Документация | Инструкция по поддержке и деплою |
| Обучение команды | Разбор кода и best practices |
Процесс работы
- Аудит кэширования — 0.5 дня.
- Проектирование стратегии — 0.5 дня.
- Реализация на iOS и Android — 1-2 дня.
- Тестирование на реальных обновлениях — 0.5 дня.
- Деплой и мониторинг — 0.5 дня.
Сроки
Базовая инвалидация кэша при обновлении: от 0,5 дня. С версионированными ключами, сохранением важного кэша и фоновой очисткой: от 1 дня. Для сложных проектов с несколькими источниками кэша: от 2 до 3 дней.
Чек-лист миграции кэша
Чек-лист миграции кэша
- [ ] Версионирование ключей для изображений и API
- [ ] Код очистки кэша при старте (в зависимости от версии)
- [ ] Очистка HTTP-кэша (URLCache / HttpCache)
- [ ] Фоновая очистка старых файлов (не блокирует запуск)
- [ ] Тест: обновление с версии N-1 до N и проверка данных
- [ ] Мониторинг крашей после обновления (Crashlytics)
Версионирование ключей надёжнее полной очистки кэша — оно не требует удаления всех файлов, что экономит время старта. Однако старые файлы всё равно необходимо удалять при старте, чтобы не заполнить хранилище.
Наши инженеры имеют 10+ лет опыта в мобильной разработке и гарантируют корректную миграцию кэша без потери данных. Свяжитесь с нами для аудита текущей системы кэширования. Получите консультацию по решению проблем с кэшем. Экономия времени на разработку — до 2 дней, а снижение затрат на поддержку покрывает стоимость реализации в течение месяца.







